Compare commits

...

249 Commits

Author SHA1 Message Date
Meghan Denny
7fdcd8e262 windows: fix fs-promises-writeFile-async-iterator.test.ts 2024-08-06 20:59:07 -07:00
Jarred Sumner
df33f2b2a2 Make getIfPropertyExists binding safer (#13134) 2024-08-06 19:23:01 -07:00
Ashcon Partovi
923303047f Fix S3 upload URL for canary assets 2024-08-06 18:51:06 -07:00
Jarred Sumner
3876ecfde8 Add test for calling websocket server publish/send methods repeatedly on closed sockets (#13131) 2024-08-06 16:22:21 -07:00
dave caruso
2680deb5d3 feat: bun build --compile --sourcemap (#13047) 2024-08-06 13:51:11 -07:00
Jarred Sumner
e1aadd0d7a Fix missing user-provided reason in fs.watch abort (#13118) 2024-08-06 00:37:17 -07:00
Jarred Sumner
7a6efad44e Skip creating JSValue for abort when it's not necessary in more cases (#13117) 2024-08-06 00:37:03 -07:00
dave caruso
4ed0c36063 fix(bundler): handle assigning to exports (#13119) 2024-08-06 00:33:32 -07:00
Meghan Denny
b75c605a75 node:http: fix assertion when request() is given options.headers thats non-object (#13112) 2024-08-05 23:54:51 -07:00
Jarred Sumner
7da9e7c45d Add test to #13082 and use WTF_MAKE_FAST_ALLOCATED (#13105)
Co-authored-by: FuPeiJiang <42662615+FuPeiJiang@users.noreply.github.com>
2024-08-05 22:07:49 -07:00
dave caruso
30d06dec47 fix(bundler): use visited enum value (#13101) 2024-08-05 18:35:04 -07:00
Ashcon Partovi
3674493aa4 Potential fix for canary artifacts missing 2024-08-05 17:49:12 -07:00
Jarred Sumner
cacbaba524 Make signal.abort() from native code fast (#13064) 2024-08-05 15:50:36 -07:00
Jarred Sumner
0d7d789ebd Implement aborted() in node:util and getEventListeners in node:events (#13100) 2024-08-05 15:47:52 -07:00
Jarred Sumner
1aa35089d6 Enable more sanitizers and fix mimalloc debug configuration (#13086) 2024-08-04 21:25:00 -07:00
Jarred Sumner
1de1745085 Bump LLVM 18 in C++ lint 2024-08-04 21:24:31 -07:00
Jarred Sumner
639e9a83d5 Add nullability annotations (#13048) 2024-08-04 21:16:41 -07:00
Jarred Sumner
9db3379cc5 Fix missing source code preview in Next.js dev server (#13073) 2024-08-04 20:13:27 -07:00
Jarred Sumner
c5c55c7ce4 Fixes #13049 (#13069) 2024-08-04 19:50:53 -07:00
Jarred Sumner
43326b0b2d Fixes #12894 (#13067) 2024-08-04 19:49:49 -07:00
Jarred Sumner
680f842948 Fix missing error log in Bun.serve (#13066) 2024-08-04 17:54:43 -07:00
Ciro Spaciari
363a4934d0 fix(server) (#13078) 2024-08-04 17:34:31 -07:00
Andrew Johnston
98f9e276b0 fix(build): retry webkit download on failure, resume download with curl (#13061)
Co-authored-by: Andrew Johnston <andrew@bun.sh>
2024-08-04 09:00:00 -07:00
guest271314
ce1286efef Try to fix formatting in rendered guides document (#13077) 2024-08-04 08:59:00 -07:00
guest271314
fd84ace83b Document Bun supports Import Attributes and JSON modules syntax (#13074) 2024-08-04 07:44:38 -07:00
Andrew Johnston
483af7c33c fix (worker-eval): fail worker with source when eval = false (#13062)
Co-authored-by: Andrew Johnston <andrew@bun.sh>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-08-03 23:49:36 -07:00
Meghan Denny
6fbe3d8214 properly propogate exit code if process onexit handler throws (#13058) 2024-08-03 16:29:52 -07:00
Ciro Spaciari
c552cb40d1 fix(server/tls/streams) fix onReadFile, streams, avoid shutdown on fatal errors, ensure ssl loop data and server ref count refactor (#12979) 2024-08-03 01:41:18 -07:00
Jarred Sumner
63cf732ab4 Support async iterators in fs.promises.writeFile (#13044) 2024-08-02 23:05:48 -07:00
Dylan Conway
6303af3ce0 fix(TextDecoder): decoding sequences starting with 192 or 193 (#13043) 2024-08-02 23:01:34 -07:00
Ashcon Partovi
9104bd7210 Fix debug builds on macOS 2024-08-02 13:02:14 -07:00
Jarred Sumner
b5c91a4b7e Upgrade WebKit (#12873)
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
2024-08-02 04:58:31 -07:00
Jarred Sumner
82239371ab Enable -Werror=int-conversion and -Werror=nonnull (#13025) 2024-08-02 01:59:08 -07:00
Jarred Sumner
26526cba38 Fix UDP socket tests on macOS 13. (#13022) 2024-08-02 00:21:57 -07:00
Jarred Sumner
214b3ccca0 Make zig cache dir relative to the cmake dir instead of global 2024-08-01 21:04:52 -07:00
dave caruso
ada020b69f fix(bundler): printing e_commonjs_export_identifier when it got deoptimized. (#13017) 2024-08-01 21:02:54 -07:00
Ashcon Partovi
deb6ff5e6c Fix typo 2024-08-01 18:30:14 -07:00
Ashcon Partovi
f25599a6e8 Fix LTO setting on Linux 2024-08-01 18:29:06 -07:00
Ashcon Partovi
de64683b22 Fix ccache environment variable 2024-08-01 18:22:52 -07:00
Ashcon Partovi
c6d508972f Deflake some build issues
* Disable sccache on Windows
* Add workaround for EBUSY/UNKNOWN spawn errors
2024-08-01 18:20:20 -07:00
Jarred Sumner
2f30e19835 Disable LTO on Windows and macOS in BuildKite 2024-08-01 18:04:25 -07:00
Meghan Denny
0081ab4738 ci: disable BUN_ENABLE_CRASH_REPORTING (#13013) 2024-08-01 17:49:43 -07:00
Jarred Sumner
6f6ea0d6f3 Add -Xclang -fno-c++-static-destructors on Windows (#13014) 2024-08-01 17:49:01 -07:00
dave caruso
622432e843 feat(bundler): inlining/dead-code-elimination for import.meta.main (and --compile) (#12867)
Co-authored-by: Meghan Denny <meghan@bun.sh>
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
Co-authored-by: dylan-conway <dylan-conway@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: paperdave <paperdave@users.noreply.github.com>
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
Co-authored-by: Andrew Johnston <apjohnsto@gmail.com>
Co-authored-by: Ashcon Partovi <ashcon@partovi.net>
2024-08-01 17:25:38 -07:00
dave caruso
80eb6d00e8 followup to recent feedback (#13009) 2024-08-01 17:21:24 -07:00
Meghan Denny
b6715d2c64 test/package-json-lint.test.ts: add back test/package.json to suite (#13011) 2024-08-01 16:56:29 -07:00
Meghan Denny
f371a78568 fix test/package-json-lint.test.ts (#13010) 2024-08-01 16:47:53 -07:00
dave caruso
c2cf528953 bundler: Add --ignore-dce-annotations, and other DCE annotation related stuff (#12808)
Co-authored-by: paperdave <paperdave@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-08-01 15:00:38 -07:00
Jarred Sumner
9911407f26 Sort heapStats() object type counts (#12989) 2024-08-01 14:09:54 -07:00
Jarred Sumner
59c5c0fe48 Fix memory leak when requiring or importing modules that get GC'd later (#12997) 2024-08-01 12:05:37 -07:00
dave caruso
e585f900c9 escape windows in bun upgrade (#12985) 2024-08-01 01:04:20 -07:00
Jarred Sumner
dc620ea837 Fix a small memory leak when requiring CommonJS modules (#12984) 2024-07-31 22:30:01 -07:00
Jarred Sumner
49ab4c147a Shrink the list of setTimeout/setInterval timers after awhile (#12957) 2024-07-31 20:50:34 -07:00
Ciro Spaciari
b2a4df68c3 fix(server) fix extra data sent in HTTP after sendfile + Date headers (#12978)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-07-31 20:22:52 -07:00
Jarred Sumner
4c0a1f2983 Add a note 2024-07-31 18:38:19 -07:00
Ciro Spaciari
bec04c7341 change Body.Value.Error to not always allocate JSValues (#12955)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-07-31 02:26:14 -07:00
Jarred Sumner
a44b7e41d2 Pass --force to git submodule update in CI 2024-07-30 23:03:35 -07:00
Jarred Sumner
de5e56336c Use example.com as the test domain in a test 2024-07-30 22:44:47 -07:00
Ciro Spaciari
1c648063fa fix(tls/socket/fetch) shutdown fix + ref counted context (#12925)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-07-30 22:41:54 -07:00
Jarred Sumner
1c3354bc95 Deflake setInterval test 2024-07-30 22:33:04 -07:00
dave caruso
d5d4f53e82 fix(bundler): put unwrapped cjs imports at top level for minifyrenamer (#12951) 2024-07-30 21:30:09 -07:00
Pipythonmc
7ab4dc738f doc: fix incorrect documentation relating to the --define flag (#12952) 2024-07-30 19:17:32 -07:00
dave caruso
ebc7045ca4 fix crash handler test failures (#12932) 2024-07-30 16:52:59 -07:00
Ashcon Partovi
848ad19d9e Attempt to fix flaky Windows builds 2024-07-30 10:55:13 -07:00
Ashcon Partovi
1da3436266 Attempt to fix flaky build-deps on Windows 2024-07-30 10:48:24 -07:00
Ashcon Partovi
49e496399a Attempt to fix missing GitHub assets on release 2024-07-30 10:44:44 -07:00
Ashcon Partovi
9b8340a5b3 Attempt to fix 'spawn error' on Windows tests 2024-07-29 19:16:29 -07:00
Meghan Denny
8efcc61a7b windows: cleanup logging of NODE_CHANNEL_FD (#12930) 2024-07-29 19:16:02 -07:00
Meghan Denny
4d6480050c NodeError: add more and use them in child_process and dgram (#12929) 2024-07-29 19:15:23 -07:00
Meghan Denny
fc2c134bc6 test: rewrite "should call close" to use promise instead of done (#12931) 2024-07-29 19:05:44 -07:00
m1212e
4c4db1da37 docs: Add hint for memory timeline in debugger (#12923) 2024-07-29 19:05:13 -07:00
dave caruso
77e14c8482 fix template addition folding 12904 (#12928) 2024-07-29 19:04:59 -07:00
Ashcon Partovi
fba5d65003 Make the release script faster 2024-07-29 17:38:06 -07:00
Jarred Sumner
c181cf45a7 Fixes #12910 (#12911) 2024-07-29 17:19:47 -07:00
Ashcon Partovi
5aeb4d9f79 Fix missing assert in release script 2024-07-29 16:57:22 -07:00
Dariush Alipour
1d9a8b4134 fix: child_process test with specified shell for windows (#12926) 2024-07-29 16:36:46 -07:00
Ashcon Partovi
30881444df Fix flaky C++ build with missing submodules 2024-07-29 16:34:01 -07:00
Ashcon Partovi
a2b4e3d4c2 Fix syntax in env.ps1 2024-07-29 16:25:50 -07:00
Ashcon Partovi
e5662caa33 Fix release script, again 2024-07-29 16:21:55 -07:00
Ashcon Partovi
1f1ea7bf24 Fix release script 2024-07-29 15:29:46 -07:00
Ashcon Partovi
175746e569 Only upload canary artifacts when the build is canary 2024-07-29 14:54:29 -07:00
Ashcon Partovi
005dd776b6 Allow creating release builds with 'RELEASE=1' 2024-07-29 14:50:26 -07:00
Ashcon Partovi
81dec2657f Enable buildkite (#12653) 2024-07-29 14:39:50 -07:00
Jarred Sumner
dbd320ccfa Remove some dynamic memory allocations in uWebSockets (#12897) 2024-07-29 15:10:55 -03:00
Jarred Sumner
8f8d3968a3 Enable concurrent transpiler on Windows (#12915) 2024-07-29 06:25:38 -07:00
Jarred Sumner
0bbdd880e6 Fix various Windows build issues 2024-07-29 04:31:39 -07:00
Jarred Sumner
51257d5668 Add BUN_FEATURE_FLAG_DISABLE_ASYNC_TRANSPILER feature flag 2024-07-29 01:37:59 -07:00
Jarred Sumner
a2ae28d158 Add named allocator 2024-07-28 21:27:08 -07:00
Jarred Sumner
f04991f6bb Fix debug build issue 2024-07-28 19:38:03 -07:00
Andrew Johnston
80e651aca3 fix(build): use specific version of lld for link on unix (#12907) 2024-07-28 18:38:01 -07:00
Jarred Sumner
a5ba02804f Use typed allocators in more places (#12899) 2024-07-28 18:37:35 -07:00
Jarred Sumner
4199fd4515 Fix memory leak in RuntimeTranspilerStore (#12900) 2024-07-28 08:30:32 -07:00
Dylan Conway
848327d333 textencoder: remove DOMJIT (#12868) 2024-07-28 07:46:53 -07:00
Jarred Sumner
bfb72f84c4 In debug builds on macOS, add malloc_zone_check when GC runs 2024-07-28 05:13:50 -07:00
Jarred Sumner
e4022ec3c7 Bump versions of things 2024-07-27 02:02:48 -07:00
Jarred Sumner
a7f34c15fc Slightly better error.stack (#12861) 2024-07-27 01:02:46 -07:00
Meghan Denny
a0ebb051b0 implement node:util.getSystemErrorName() (#12837) 2024-07-27 00:20:50 -07:00
dave caruso
70ca2b76c3 fix: check if we are crashing before exiting gracefully (#12865) 2024-07-26 20:00:02 -07:00
Jarred Sumner
e5ac4f94fa Handle errors in node:http better (#12641)
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2024-07-26 19:53:36 -07:00
dave caruso
d547d8a30e fix a bundler crash (#12864)
Co-authored-by: paperdave <paperdave@users.noreply.github.com>
2024-07-26 19:39:37 -07:00
Meghan Denny
32d9bb3ced ci: format: switch to mlugg/setup-zig (#12863) 2024-07-26 18:47:02 -07:00
dave caruso
75df73ef90 fix: make raiseIgnoringPanicHandler ignore the panic handler (#12578)
Co-authored-by: paperdave <paperdave@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-07-26 18:36:53 -07:00
Dylan Conway
13907c4c29 fix(build): assertion failure when cross-compiling on windows (#12862)
Co-authored-by: dylan-conway <dylan-conway@users.noreply.github.com>
2024-07-26 17:29:01 -07:00
Jarred Sumner
87169b6bb3 Configure libcpp assert to avoid macOS 13.0 issue (#12860) 2024-07-26 16:03:16 -07:00
Jarred Sumner
244100c32f When crash reporter is disabled also disable resetSegfaultHanlder 2024-07-26 14:50:56 -07:00
Jarred Sumner
8a78b2241d Rename JSC.Node.StringOrBuffer -> StringOrBuffer 2024-07-26 14:50:30 -07:00
dave caruso
bf8b6922bb Fix memory leak when printing any error's source code. (#12831)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-07-26 14:14:16 -07:00
Dylan Conway
7aa05ec542 bump webkit (#12858)
Co-authored-by: dylan-conway <dylan-conway@users.noreply.github.com>
2024-07-26 14:13:58 -07:00
Meghan Denny
f95ae9baee launch.json: remove BUN_DEBUG_ALL=1 from 'bun run' (#12845) 2024-07-26 04:14:45 -07:00
Meghan Denny
f7cb2da542 use .undefined literal instead of jsUndefined() call (#12834) 2024-07-26 04:03:55 -07:00
Meghan Denny
d966129992 bindings: better use of jsc api in Path_functionToNamespacedPath (#12836) 2024-07-26 04:02:31 -07:00
Meghan Denny
6cb5cd2a87 node:v8: expose DefaultDeserializer and DefaultSerializer exports (#12838) 2024-07-26 03:58:47 -07:00
Meghan Denny
080a2806af uws: tidy use of ssl intFromBool (#12839) 2024-07-26 03:58:01 -07:00
Meghan Denny
92c83fcd9e ipc: make IPCInstance.context void on windows instead of u0 (#12840) 2024-07-26 03:56:13 -07:00
Meghan Denny
277ed9d138 bindings: fix zig extern def of Bun__JSValue__deserialize (#12844) 2024-07-26 03:48:30 -07:00
Meghan Denny
879cb23163 cpp: missing uses of propertyNames (#12835) 2024-07-26 03:47:41 -07:00
Jarred Sumner
d321ee97c5 Move napi_new_instance to c++ (#12658)
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
2024-07-25 18:51:01 -07:00
Jarred Sumner
3bfeb83e7e Fix [Symbol.dispose] on Bun.listen() & Bun.connect() + add types (#12739)
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
2024-07-25 18:49:35 -07:00
Meghan Denny
5a18b7d2fc fixes relationship between process.kill and process._kill (#12792)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-07-25 18:46:30 -07:00
Gert Goet
e75ef69fb4 Fix spacing of patch-command help (#12769) 2024-07-25 18:44:52 -07:00
Dylan Conway
78021e34ae fix(bun:test): make sure test.each doesn't return .zero (#12828) 2024-07-25 18:24:16 -07:00
Dylan Conway
d7187592c0 fix(brotli): protect and unprotect buffer values (#12829) 2024-07-25 18:24:01 -07:00
Jarred Sumner
5f1b569c52 Fix crash when creating a new Worker with a numeric environment varia… (#12810) 2024-07-25 18:10:57 -07:00
dave caruso
e54fe5995b fix(bundler): dont tree-shake imported enum if inlined and used (#12826) 2024-07-25 17:29:20 -07:00
Jarred Sumner
a2f68989a0 Retry on 5xx errors from npm registry (#12825) 2024-07-25 17:28:59 -07:00
Jarred Sumner
4a1e01d076 Use bun.New more (#12811)
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
2024-07-25 15:35:02 -07:00
Jarred Sumner
dd8b0a5889 Clean up socket_async_http_abort_tracker after a lot of requests (#12816) 2024-07-25 15:34:48 -07:00
Jarred Sumner
8cadf66143 Add malloc zones to heapStats() on macOS in debug builds (#12815) 2024-07-25 15:33:43 -07:00
Jarred Sumner
77cd03dad1 Workaround for BUN-2WQ (#12806) 2024-07-25 15:33:17 -07:00
Meghan Denny
82b42ed851 node: more process.exitCode fixes (#12809) 2024-07-25 15:18:41 -07:00
Dylan Conway
2de82c0b3b fix regression test 2024-07-25 14:07:57 -07:00
Jarred Sumner
30df04cd35 Add a couple feature flags 2024-07-25 05:52:50 -07:00
Jarred Sumner
585c8299d8 Clean up some stack trace printing logic (#12791) 2024-07-25 04:04:02 -07:00
Dylan Conway
375d8da8e6 fix(brotli): protect buffer jsvalues (#12800)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-07-25 04:01:53 -07:00
Jarred Sumner
0bd8db7162 Slightly reduce pointer lookups in hot code path (#12802) 2024-07-24 23:07:51 -07:00
Meghan Denny
d97260869d replace JSValue .callWithThis with always explicit .call (#12789) 2024-07-24 23:07:28 -07:00
Jarred Sumner
fdb58dc861 Fixes #9555 (#12801) 2024-07-24 23:07:04 -07:00
Meghan Denny
5f118704ec web-apis.md: make this list diff better; does not change presentation (#12795) 2024-07-24 22:27:32 -07:00
dave caruso
610c7f5e47 fix memory lifetime of define expressions (#12784) 2024-07-24 22:27:14 -07:00
Meghan Denny
1e0b20f514 node: fix observable value of process.exitCode (#12799) 2024-07-24 22:26:07 -07:00
Meghan Denny
f6c89f4c25 JSValue.toFmt doesn't need a globalThis param because ConsoleObject.Formatter already has one (#12790) 2024-07-24 22:03:51 -07:00
Meghan Denny
907cd8d45d fix crash in populateStackTrace() (#12793) 2024-07-24 21:08:25 -07:00
Dylan Conway
ac4523e903 fix(napi): unref threadsafe functions on finalize (#12788) 2024-07-24 18:57:01 -07:00
Jarred Sumner
24574dddb2 Ensure LLVM 18 with Homebrew on macOS 2024-07-24 15:56:05 -07:00
Dylan Conway
2da57f6d7b napi_threadsafe_function async tracker (#12780) 2024-07-24 15:27:51 -07:00
dave caruso
e2c3749965 fix(bundler): become smarter with __esm wrappers (#12729) 2024-07-24 02:00:20 -07:00
Jarred Sumner
57c6a7db35 libdeflate (#12741) 2024-07-24 01:30:31 -07:00
ippsav
c37891471a Fix alignment calculation in Zone.create function (#12748) 2024-07-24 01:30:11 -07:00
Jarred Sumner
8ba0791dc8 Use one JSC::SourceProvider instead of 322 (#12761) 2024-07-24 01:26:09 -07:00
dave caruso
f9371e59f2 fix(bundler): fix part liveness calculation (#12758) 2024-07-23 23:49:01 -07:00
Jarred Sumner
79ddf0e47a Fix assertion failure in bun build when entry point is a file loader (#12683)
Co-authored-by: dave caruso <me@paperdave.net>
2024-07-23 22:02:51 -07:00
David Stevens
177f3a8622 Fixes #12182 - update default port when server is created (#12201) 2024-07-23 11:07:38 -07:00
Ciro Spaciari
5a5f3d6b30 fix(http) timeout (#12728)
Co-authored-by: cirospaciari <cirospaciari@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-07-23 01:13:43 -07:00
Ivan Baksheev
4e5d759c37 fix(bundler): ignore external rules for entrypoint (#12736) 2024-07-23 00:33:18 -07:00
Jarred Sumner
1a702dfdc7 Add canary to cache key 2024-07-22 22:16:27 -07:00
Jarred Sumner
3ef84816a6 Update WebKit 2024-07-22 20:47:53 -07:00
Jarred Sumner
6e9b592c56 try using LLVM 18 on macOS (#12727)
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2024-07-22 20:44:29 -07:00
Jarred Sumner
a6b5543bd8 Don't set fuse-ld=lld in boringssl script 2024-07-22 15:41:41 -07:00
Jarred Sumner
a4759eb147 Bump minimum macOS build to 13.0 2024-07-22 14:50:23 -07:00
Dariush Alipour
732ed2b7df Fix: test coverage node_modules exclusion in Windows (#12691) 2024-07-22 13:25:42 -07:00
Jarred Sumner
63fab9a82b Update fetch.md 2024-07-22 04:06:00 -07:00
Jarred Sumner
ff17b427c8 Update fetch.md 2024-07-22 04:02:48 -07:00
Jarred Sumner
ca44df7c88 Update fetch.md 2024-07-22 03:57:29 -07:00
huseeiin
9daa7ea555 Update bun.d.ts (#12719) 2024-07-22 03:55:14 -07:00
Jarred Sumner
2f0020f00f Update fetch.md 2024-07-22 03:52:33 -07:00
Jarred Sumner
599d27d93e Update fetch.md 2024-07-22 03:50:22 -07:00
Jarred Sumner
696f209ec1 Update fetch.md 2024-07-22 03:49:32 -07:00
Jarred Sumner
1a6ead667b Introduce bun --fetch-preconnect <url> ./my-script.ts (#12698) 2024-07-22 03:41:59 -07:00
Jarred Sumner
bbf2f5d716 Experiment: disable C++ static destructors (#12652)
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2024-07-21 21:32:52 -07:00
ippsav
9574044083 Fix expect.toThrow(expect.any()) matcher to correctly handle ExpectAny objects (#12670) 2024-07-21 20:35:18 -07:00
Jarred Sumner
822b725bec Fix BUN-2M9, take two 2024-07-21 08:49:00 -07:00
Jarred Sumner
dc775f75f0 Fix BUN-2M9 2024-07-21 07:40:57 -07:00
Jarred Sumner
738947bdec Deflake node-tls-connect test 2024-07-20 02:36:08 -07:00
Jarred Sumner
b7efeafc03 Deflake node-tls-connect test 2024-07-19 23:42:23 -07:00
Jarred Sumner
f5d1a17a5c Fix crash in bun exec cd (#12676)
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2024-07-19 22:57:52 -07:00
Jarred Sumner
03024e6b4e Fix truncating in BigIntStats (#12643)
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2024-07-19 20:00:32 -07:00
Jarred Sumner
1d61676c7b Check for file or directory 2024-07-18 13:56:21 -07:00
Jarred Sumner
23fb63f45c Fixes #12360 (#12364) 2024-07-18 09:10:15 -07:00
Jarred Sumner
0be71edf3f Upload .dSYM file 2024-07-18 03:19:48 -07:00
Jarred Sumner
6b50deb7b7 Move dev dependencies to "devDependencies"
Spammy vulnerability scanning software can't tell we aren't using these in the "bun" npm package.
2024-07-18 01:13:36 -07:00
Jarred Sumner
6ad3e6a5e3 Fixes #2532 (#12633)
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2024-07-17 20:53:12 -07:00
HibanaSama
b1dce1e241 build(windows): fix esbuild errors when bundling node-fallbacks (#12628) 2024-07-17 19:40:25 -07:00
Jarred Sumner
cc42052039 node-fetch polyfill shouldn't break when web globals are overriden (#12634) 2024-07-17 18:57:03 -07:00
Jarred Sumner
ecf5aea071 Ensure undici primordials are pristine (#12635) 2024-07-17 18:56:22 -07:00
Jarred Sumner
79d21a0d02 Bump internal bun versions 2024-07-17 17:21:14 -07:00
dave caruso
43949151b1 fix(bundler): importing modules with trailing slash no longer uses a builtin (#12632) 2024-07-17 17:17:00 -07:00
Ciro Spaciari
16aad326e4 fix(setSystemTime) fix number parameter behavior (#12630)
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
2024-07-17 17:09:24 -07:00
Jarred Sumner
1a6f2d38da Github actions 2024-07-17 03:31:12 -07:00
Jarred Sumner
c6149d36b3 Bump 2024-07-17 02:40:42 -07:00
Jarred Sumner
34e493f945 Experiment: disable -fPIC and relro (#12582)
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2024-07-17 02:33:46 -07:00
dave caruso
866b301626 bundler: make import() calls visit the options object (#12617) 2024-07-16 23:21:34 -07:00
dave caruso
cabc0fa0e6 fix typescript namespace merging with functions and classes (#12610) 2024-07-16 19:39:27 -07:00
HibanaSama
d703354fcd docs: close details block (#12533) 2024-07-16 17:50:47 -07:00
Ciro Spaciari
37036f2eb0 fix(serve) fix abrupt close when downloading data (#12581)
Co-authored-by: cirospaciari <cirospaciari@users.noreply.github.com>
2024-07-16 16:39:37 -07:00
190n
ff0dc62314 Accept undefined as explicit second argument for path.*.basename (#12609) 2024-07-16 16:37:21 -07:00
Ciro Spaciari
f05f13780e fix(CryptoHasher) check of .empty/null/undefined in update (#12607)
Co-authored-by: cirospaciari <cirospaciari@users.noreply.github.com>
2024-07-16 16:33:35 -07:00
190n
4d74855fd7 Prevent unref from hanging on uninitialized dgram socket (#12585)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-07-16 16:31:07 -07:00
Jarred Sumner
5088a360b5 Avoid stale reference to Body.Value when erroring (#12579) 2024-07-16 12:33:19 -07:00
dave caruso
891b1907ae feat(bundler): implement non-isolated hashes (#12576) 2024-07-15 20:34:15 -07:00
dave caruso
ae988642fb fix .use_count integer underflow (#12584) 2024-07-15 18:36:42 -07:00
190n
75e442c170 Change .mjs to .mts during TypeScript module resolution (fixes #12471) (#12580) 2024-07-15 18:12:18 -07:00
Jarred Sumner
8808af1c99 Replace some Identifier::fromString usages with vm->propertyNames (#12575) 2024-07-15 16:15:56 -07:00
Jarred Sumner
b9d2a03ffc Make creating a BufferList (used in node:stream) slightly faster (#12577) 2024-07-15 16:15:10 -07:00
Jarred Sumner
157b56cca5 Update launch.json 2024-07-15 15:12:57 -07:00
Eric L. Goldstein
caaeae123a Add documentation for mock.restore() (#12553) 2024-07-14 21:20:33 -07:00
Ivan Baksheev
20235a0d22 Add packages option to remove all dependencies from bundle (#12562) 2024-07-14 15:20:27 -07:00
Dylan Conway
ae19489250 bump 2024-07-12 19:45:10 -07:00
Dylan Conway
242c48f302 bump 2024-07-12 19:42:18 -07:00
dave caruso
110849355c make the windows binary smaller (#12523)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-07-12 00:20:28 -07:00
Jarred Sumner
36fd3115f1 Try icf=safe (#12524) 2024-07-11 22:37:04 -07:00
Jarred Sumner
7d9b876968 Use armv8a+crc 2024-07-11 20:59:37 -07:00
Jarred Sumner
40f0da1254 Use armv8-a 2024-07-11 20:30:21 -07:00
Jarred Sumner
aea3964abd Set -march instead of -mcpu 2024-07-11 20:14:43 -07:00
Jarred Sumner
780bff781d Set the cpu model in the right place 2024-07-11 19:00:06 -07:00
Jarred Sumner
c6a2ab5165 Revert "Don't set mtune"
This reverts commit ef1c660708.
2024-07-11 18:58:24 -07:00
Jarred Sumner
ef1c660708 Don't set mtune 2024-07-11 18:40:52 -07:00
Ciro Spaciari
11f8d3cb24 fix(server) fix abrupt stop (#12472)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
Co-authored-by: cirospaciari <cirospaciari@users.noreply.github.com>
2024-07-11 18:22:23 -07:00
dave caruso
3ac9c3cc1c make bun static link the c redistributable (#12521)
Co-authored-by: paperdave <paperdave@users.noreply.github.com>
2024-07-11 17:35:35 -07:00
Jarred Sumner
aa0f54cb93 Fixes #12076 (#12504) 2024-07-11 16:59:14 -07:00
huseeiin
8a3f882ef7 Update README.md (#12512) 2024-07-11 07:24:13 -07:00
Jarred Sumner
b7dd57ac32 Fix zig build error on Windows 2024-07-11 00:08:05 -07:00
Jarred Sumner
cf1c7772f3 See if .dSYM will upload (#12502) 2024-07-10 23:53:17 -07:00
Jarred Sumner
329d5e2af5 Disable this debug log 2024-07-10 23:25:55 -07:00
Jarred Sumner
0098678a1d Upload .dSYM file in CI 2024-07-10 23:04:06 -07:00
Jarred Sumner
bf4c2caa11 Bump 2024-07-10 22:48:28 -07:00
Jarred Sumner
226f42e04a Rewrite js_ast.NewBaseStore (#12388)
Co-authored-by: dave caruso <me@paperdave.net>
2024-07-10 21:57:40 -07:00
Ciro Spaciari
96d19fcfe2 fix(fetch.tls.test) make test more reliable (#12499) 2024-07-10 21:52:34 -07:00
Dylan Conway
58483426cd fix(install): call GetFinalPathNameByHandle on cwd for postinstall scripts (#12497) 2024-07-10 21:09:14 -07:00
Dylan Conway
25f7ef7338 Revert "Nest test results under describe scopes (#12189)"
This reverts commit 6a43f7f52d.
2024-07-10 21:05:47 -07:00
Jarred Sumner
412806bb22 Make expect().toThrow faster (#12494) 2024-07-10 20:46:29 -07:00
Ciro Spaciari
4c87406391 fix(ssl) fix ssl shutdown (#12492)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-07-10 20:29:54 -07:00
Dylan Conway
5f7b96b58f fix(install): optional peer dependency bugfix (#12485)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: dylan-conway <dylan-conway@users.noreply.github.com>
2024-07-10 20:04:32 -07:00
Jarred Sumner
f1151a84ad On Windows, fix fs.writeFile(1, fs.writeFile(2, fs.writeFile(\\nul (#12410)
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
2024-07-10 19:49:36 -07:00
Zack Radisic
cdc68a2237 .npmrc follow up (#12390)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
2024-07-10 19:35:21 -07:00
Jarred Sumner
e866793eb3 Fix Windows assertion failures 2024-07-10 18:08:04 -07:00
Dylan Conway
cf9c418bcb revert 2024-07-10 17:22:16 -07:00
Jarred Sumner
138ef1328e webcrypto tests are slow 2024-07-10 17:18:39 -07:00
Dylan Conway
e5e6d7ca43 comma 2024-07-10 17:03:19 -07:00
Jarred Sumner
cb81fc5445 Make ${encoding}Slice & ${encoding}Write work on Uint8Array (#12491) 2024-07-10 16:58:01 -07:00
Dylan Conway
d8caf7f9fa install all at once 2024-07-10 16:48:40 -07:00
Dylan Conway
6f8ceb0ea9 windows: bump llvm to 18.1.8 (#12490) 2024-07-10 16:45:54 -07:00
dave caruso
02b589b2ce fix a crash in remapping stacks (#12477)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-07-10 16:39:05 -07:00
Jarred Sumner
55d59ebf1f Try disabling the vs2022 build tools workaround 2024-07-10 02:01:29 -07:00
Jarred Sumner
e1bc6c55d5 Speculative fix for crash in visitChildren in BufferList (#12427) 2024-07-10 01:44:52 -07:00
Dylan Conway
e42dede529 upgrade webkit (#12474)
Co-authored-by: dylan-conway <dylan-conway@users.noreply.github.com>
2024-07-10 01:38:26 -07:00
arnab
73ef93ffa3 Fix spelling - following (#12479) 2024-07-09 23:35:06 -07:00
Derrick Farris
475f71a2a1 fix(jest): beforeEach, afterEach not called for test.todo (#12406) 2024-07-09 23:15:27 -07:00
Ciro Spaciari
af6035ce36 fix(server) wait for readFile on abort/process exit cases (#12441)
Co-authored-by: cirospaciari <cirospaciari@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-07-09 22:38:32 -07:00
Jarred Sumner
bfa395d1d5 Warn when installing a global binary and global bin is not in path (#12454) 2024-07-09 22:32:56 -07:00
560 changed files with 18877 additions and 55778 deletions

View File

@@ -10,9 +10,10 @@ steps:
blocked_state: "running"
- label: ":pipeline:"
command: "buildkite-agent pipeline upload .buildkite/ci.yml"
agents:
queue: "build-linux"
queue: "build-darwin"
command:
- ".buildkite/scripts/prepare-build.sh"
- if: "build.branch == 'main' && !build.pull_request.repository.fork"
label: ":github:"

File diff suppressed because it is too large Load Diff

55
.buildkite/scripts/build-bun.sh Executable file
View File

@@ -0,0 +1,55 @@
#!/bin/bash
set -eo pipefail
source "$(dirname "$0")/env.sh"
function run_command() {
set -x
"$@"
{ set +x; } 2>/dev/null
}
cwd="$(pwd)"
mkdir -p build
source "$(dirname "$0")/download-artifact.sh" "build/bun-deps/**" --step "$BUILDKITE_GROUP_KEY-build-deps"
source "$(dirname "$0")/download-artifact.sh" "build/bun-zig.o" --step "$BUILDKITE_GROUP_KEY-build-zig"
source "$(dirname "$0")/download-artifact.sh" "build/bun-cpp-objects.a" --step "$BUILDKITE_GROUP_KEY-build-cpp" --split
cd build
run_command cmake .. "${CMAKE_FLAGS[@]}" \
-GNinja \
-DBUN_LINK_ONLY="1" \
-DNO_CONFIGURE_DEPENDS="1" \
-DBUN_ZIG_OBJ_DIR="$cwd/build" \
-DBUN_CPP_ARCHIVE="$cwd/build/bun-cpp-objects.a" \
-DBUN_DEPS_OUT_DIR="$cwd/build/bun-deps" \
-DCMAKE_BUILD_TYPE="$CMAKE_BUILD_TYPE" \
-DCPU_TARGET="$CPU_TARGET" \
-DUSE_LTO="$USE_LTO" \
-DUSE_DEBUG_JSC="$USE_DEBUG_JSC" \
-DCANARY="$CANARY" \
-DGIT_SHA="$GIT_SHA"
run_command ninja -v -j "$CPUS"
run_command ls
tag="bun-$BUILDKITE_GROUP_KEY"
if [ "$USE_LTO" == "OFF" ]; then
# Remove OS check when LTO is enabled on macOS again
if [[ "$tag" == *"darwin"* ]]; then
tag="$tag-nolto"
fi
fi
for name in bun bun-profile; do
dir="$tag"
if [ "$name" == "bun-profile" ]; then
dir="$tag-profile"
fi
run_command chmod +x "$name"
run_command "./$name" --revision
run_command mkdir -p "$dir"
run_command mv "$name" "$dir/$name"
run_command zip -r "$dir.zip" "$dir"
source "$cwd/.buildkite/scripts/upload-artifact.sh" "$dir.zip"
done

35
.buildkite/scripts/build-cpp.sh Executable file
View File

@@ -0,0 +1,35 @@
#!/bin/bash
set -eo pipefail
source "$(dirname "$0")/env.sh"
export FORCE_UPDATE_SUBMODULES=1
source "$(realpath $(dirname "$0")/../../scripts/update-submodules.sh)"
{ set +x; } 2>/dev/null
function run_command() {
set -x
"$@"
{ set +x; } 2>/dev/null
}
mkdir -p build
cd build
mkdir -p tmp_modules tmp_functions js codegen
run_command cmake .. "${CMAKE_FLAGS[@]}" \
-GNinja \
-DBUN_CPP_ONLY="1" \
-DNO_CONFIGURE_DEPENDS="1" \
-DCMAKE_BUILD_TYPE="$CMAKE_BUILD_TYPE" \
-DCPU_TARGET="$CPU_TARGET" \
-DUSE_LTO="$USE_LTO" \
-DUSE_DEBUG_JSC="$USE_DEBUG_JSC" \
-DCANARY="$CANARY" \
-DGIT_SHA="$GIT_SHA"
chmod +x compile-cpp-only.sh
source compile-cpp-only.sh -v -j "$CPUS"
{ set +x; } 2>/dev/null
cd ..
source "$(dirname "$0")/upload-artifact.sh" "build/bun-cpp-objects.a" --split

View File

View File

@@ -0,0 +1,22 @@
#!/bin/bash
set -eo pipefail
source "$(dirname "$0")/env.sh"
source "$(realpath $(dirname "$0")/../../scripts/all-dependencies.sh)"
artifacts=(
libcrypto.a libssl.a libdecrepit.a
libcares.a
libarchive.a
liblolhtml.a
libmimalloc.a libmimalloc.o
libtcc.a
libz.a
libzstd.a
libdeflate.a
liblshpack.a
)
for artifact in "${artifacts[@]}"; do
source "$(dirname "$0")/upload-artifact.sh" "build/bun-deps/$artifact"
done

View File

@@ -0,0 +1,40 @@
#!/bin/bash
set -eo pipefail
source "$(dirname "$0")/env.sh"
function assert_bun() {
if ! command -v bun &>/dev/null; then
echo "error: bun is not installed" 1>&2
exit 1
fi
}
function assert_make() {
if ! command -v make &>/dev/null; then
echo "error: make is not installed" 1>&2
exit 1
fi
}
function run_command() {
set -x
"$@"
{ set +x; } 2>/dev/null
}
function build_node_fallbacks() {
local cwd="src/node-fallbacks"
run_command bun install --cwd "$cwd" --frozen-lockfile
run_command bun run --cwd "$cwd" build
}
function build_old_js() {
run_command bun install --frozen-lockfile
run_command make runtime_js fallback_decoder bun_error
}
assert_bun
assert_make
build_node_fallbacks
build_old_js

80
.buildkite/scripts/build-zig.sh Executable file
View File

@@ -0,0 +1,80 @@
#!/bin/bash
set -eo pipefail
source "$(dirname "$0")/env.sh"
function assert_target() {
local arch="${2-$(uname -m)}"
case "$(echo "$arch" | tr '[:upper:]' '[:lower:]')" in
x64 | x86_64 | amd64)
export ZIG_ARCH="x86_64"
if [[ "$BUILDKITE_STEP_KEY" == *"baseline"* ]]; then
export ZIG_CPU_TARGET="nehalem"
else
export ZIG_CPU_TARGET="haswell"
fi
;;
aarch64 | arm64)
export ZIG_ARCH="aarch64"
export ZIG_CPU_TARGET="native"
;;
*)
echo "error: Unsupported architecture: $arch" 1>&2
exit 1
;;
esac
local os="${1-$(uname -s)}"
case "$(echo "$os" | tr '[:upper:]' '[:lower:]')" in
linux)
export ZIG_TARGET="$ZIG_ARCH-linux-gnu" ;;
darwin)
export ZIG_TARGET="$ZIG_ARCH-macos-none" ;;
windows)
export ZIG_TARGET="$ZIG_ARCH-windows-msvc" ;;
*)
echo "error: Unsupported operating system: $os" 1>&2
exit 1
;;
esac
}
function run_command() {
set -x
"$@"
{ set +x; } 2>/dev/null
}
assert_target "$@"
# Since the zig build depends on files from the zig submodule,
# make sure to update the submodule before building.
run_command git submodule update --init --recursive --progress --depth=1 --checkout src/deps/zig
# TODO: Move these to be part of the CMake build
source "$(dirname "$0")/build-old-js.sh"
cwd="$(pwd)"
mkdir -p build
cd build
run_command cmake .. "${CMAKE_FLAGS[@]}" \
-GNinja \
-DNO_CONFIGURE_DEPENDS="1" \
-DNO_CODEGEN="0" \
-DWEBKIT_DIR="omit" \
-DBUN_ZIG_OBJ_DIR="$cwd/build" \
-DZIG_LIB_DIR="$cwd/src/deps/zig/lib" \
-DCMAKE_BUILD_TYPE="$CMAKE_BUILD_TYPE" \
-DARCH="$ZIG_ARCH" \
-DCPU_TARGET="$ZIG_CPU_TARGET" \
-DZIG_TARGET="$ZIG_TARGET" \
-DUSE_LTO="$USE_LTO" \
-DUSE_DEBUG_JSC="$USE_DEBUG_JSC" \
-DCANARY="$CANARY" \
-DGIT_SHA="$GIT_SHA"
export ONLY_ZIG="1"
run_command ninja "$cwd/build/bun-zig.o" -v -j "$CPUS"
cd ..
source "$(dirname "$0")/upload-artifact.sh" "build/bun-zig.o"

View File

@@ -0,0 +1,47 @@
param (
[Parameter(Mandatory=$true)]
[string[]] $Paths,
[switch] $Split
)
$ErrorActionPreference = "Stop"
function Assert-Buildkite-Agent() {
if (-not (Get-Command "buildkite-agent" -ErrorAction SilentlyContinue)) {
Write-Error "Cannot find buildkite-agent, please install it: https://buildkite.com/docs/agent/v3/install"
exit 1
}
}
function Assert-Join-File() {
if (-not (Get-Command "Join-File" -ErrorAction SilentlyContinue)) {
Write-Error "Cannot find Join-File, please install it: https://www.powershellgallery.com/packages/FileSplitter/1.3"
exit 1
}
}
function Download-Buildkite-Artifact() {
param (
[Parameter(Mandatory=$true)]
[string] $Path,
)
if ($Split) {
& buildkite-agent artifact download "$Path.*" --debug --debug-http
Join-File -Path "$(Resolve-Path .)\$Path" -Verbose -DeletePartFiles
} else {
& buildkite-agent artifact download "$Path" --debug --debug-http
}
if (-not (Test-Path $Path)) {
Write-Error "Could not find artifact: $Path"
exit 1
}
}
Assert-Buildkite-Agent
if ($Split) {
Assert-Join-File
}
foreach ($Path in $Paths) {
Download-Buildkite-Artifact $Path
}

View File

@@ -0,0 +1,46 @@
#!/bin/bash
set -eo pipefail
function assert_buildkite_agent() {
if ! command -v buildkite-agent &> /dev/null; then
echo "error: Cannot find buildkite-agent, please install it:"
echo "https://buildkite.com/docs/agent/v3/install"
exit 1
fi
}
function download_buildkite_artifact() {
local path="$1"; shift
local split="0"
local args=()
while true; do
if [ -z "$1" ]; then
break
fi
case "$1" in
--split) split="1"; shift ;;
*) args+=("$1"); shift ;;
esac
done
if [ "$split" == "1" ]; then
run_command buildkite-agent artifact download "$path.*" . "${args[@]}"
run_command cat $path.?? > "$path"
run_command rm -f $path.??
else
run_command buildkite-agent artifact download "$path" . "${args[@]}"
fi
if [[ "$path" != *"*"* ]] && [ ! -f "$path" ]; then
echo "error: Could not find artifact: $path"
exit 1
fi
}
function run_command() {
set -x
"$@"
{ set +x; } 2>/dev/null
}
assert_buildkite_agent
download_buildkite_artifact "$@"

120
.buildkite/scripts/env.sh Executable file
View File

@@ -0,0 +1,120 @@
#!/bin/bash
set -eo pipefail
function assert_os() {
local os="$(uname -s)"
case "$os" in
Linux)
echo "linux" ;;
Darwin)
echo "darwin" ;;
*)
echo "error: Unsupported operating system: $os" 1>&2
exit 1
;;
esac
}
function assert_arch() {
local arch="$(uname -m)"
case "$arch" in
aarch64 | arm64)
echo "aarch64" ;;
x86_64 | amd64)
echo "x64" ;;
*)
echo "error: Unknown architecture: $arch" 1>&2
exit 1
;;
esac
}
function assert_build() {
if [ -z "$BUILDKITE_REPO" ]; then
echo "error: Cannot find repository for this build"
exit 1
fi
if [ -z "$BUILDKITE_COMMIT" ]; then
echo "error: Cannot find commit for this build"
exit 1
fi
if [ -z "$BUILDKITE_STEP_KEY" ]; then
echo "error: Cannot find step key for this build"
exit 1
fi
if [ -n "$BUILDKITE_GROUP_KEY" ] && [[ "$BUILDKITE_STEP_KEY" != "$BUILDKITE_GROUP_KEY"* ]]; then
echo "error: Build step '$BUILDKITE_STEP_KEY' does not start with group key '$BUILDKITE_GROUP_KEY'"
exit 1
fi
# Skip os and arch checks for Zig, since it's cross-compiled on macOS
if [[ "$BUILDKITE_STEP_KEY" != *"zig"* ]]; then
local os="$(assert_os)"
if [[ "$BUILDKITE_STEP_KEY" != *"$os"* ]]; then
echo "error: Build step '$BUILDKITE_STEP_KEY' does not match operating system '$os'"
exit 1
fi
local arch="$(assert_arch)"
if [[ "$BUILDKITE_STEP_KEY" != *"$arch"* ]]; then
echo "error: Build step '$BUILDKITE_STEP_KEY' does not match architecture '$arch'"
exit 1
fi
fi
}
function assert_buildkite_agent() {
if ! command -v buildkite-agent &> /dev/null; then
echo "error: Cannot find buildkite-agent, please install it:"
echo "https://buildkite.com/docs/agent/v3/install"
exit 1
fi
}
function export_environment() {
source "$(realpath $(dirname "$0")/../../scripts/env.sh)"
source "$(realpath $(dirname "$0")/../../scripts/update-submodules.sh)"
{ set +x; } 2>/dev/null
export GIT_SHA="$BUILDKITE_COMMIT"
export CCACHE_DIR="$HOME/.cache/ccache/$BUILDKITE_STEP_KEY"
export SCCACHE_DIR="$HOME/.cache/sccache/$BUILDKITE_STEP_KEY"
export ZIG_LOCAL_CACHE_DIR="$HOME/.cache/zig-cache/$BUILDKITE_STEP_KEY"
export ZIG_GLOBAL_CACHE_DIR="$HOME/.cache/zig-cache/$BUILDKITE_STEP_KEY"
export BUN_DEPS_CACHE_DIR="$HOME/.cache/bun-deps/$BUILDKITE_STEP_KEY"
if [ "$(assert_os)" == "linux" ]; then
export USE_LTO="ON"
fi
if [ "$(assert_arch)" == "aarch64" ]; then
export CPU_TARGET="native"
elif [[ "$BUILDKITE_STEP_KEY" == *"baseline"* ]]; then
export CPU_TARGET="nehalem"
else
export CPU_TARGET="haswell"
fi
if $(buildkite-agent meta-data exists release &> /dev/null); then
export CMAKE_BUILD_TYPE="$(buildkite-agent meta-data get release)"
else
export CMAKE_BUILD_TYPE="Release"
fi
if $(buildkite-agent meta-data exists canary &> /dev/null); then
export CANARY="$(buildkite-agent meta-data get canary)"
else
export CANARY="1"
fi
if $(buildkite-agent meta-data exists assertions &> /dev/null); then
export USE_DEBUG_JSC="$(buildkite-agent meta-data get assertions)"
else
export USE_DEBUG_JSC="OFF"
fi
if [ "$BUILDKITE_CLEAN_CHECKOUT" == "true" || "$BUILDKITE_BRANCH" == "main" ]; then
rm -rf "$CCACHE_DIR"
rm -rf "$SCCACHE_DIR"
rm -rf "$ZIG_LOCAL_CACHE_DIR"
rm -rf "$ZIG_GLOBAL_CACHE_DIR"
rm -rf "$BUN_DEPS_CACHE_DIR"
export CCACHE_RECACHE="1"
fi
}
assert_build
assert_buildkite_agent
export_environment

View File

@@ -0,0 +1,97 @@
#!/bin/bash
set -eo pipefail
function assert_build() {
if [ -z "$BUILDKITE_REPO" ]; then
echo "error: Cannot find repository for this build"
exit 1
fi
if [ -z "$BUILDKITE_COMMIT" ]; then
echo "error: Cannot find commit for this build"
exit 1
fi
}
function assert_buildkite_agent() {
if ! command -v buildkite-agent &> /dev/null; then
echo "error: Cannot find buildkite-agent, please install it:"
echo "https://buildkite.com/docs/agent/v3/install"
exit 1
fi
}
function assert_jq() {
assert_command "jq" "jq" "https://stedolan.github.io/jq/"
}
function assert_curl() {
assert_command "curl" "curl" "https://curl.se/download.html"
}
function assert_command() {
local command="$1"
local package="$2"
local help_url="$3"
if ! command -v "$command" &> /dev/null; then
echo "warning: $command is not installed, installing..."
if command -v brew &> /dev/null; then
HOMEBREW_NO_AUTO_UPDATE=1 brew install "$package"
else
echo "error: Cannot install $command, please install it"
if [ -n "$help_url" ]; then
echo ""
echo "hint: See $help_url for help"
fi
exit 1
fi
fi
}
function assert_release() {
if [ "$RELEASE" == "1" ]; then
run_command buildkite-agent meta-data set canary "0"
fi
}
function assert_canary() {
local canary="$(buildkite-agent meta-data get canary 2>/dev/null)"
if [ -z "$canary" ]; then
local repo=$(echo "$BUILDKITE_REPO" | sed -E 's#https://github.com/([^/]+)/([^/]+).git#\1/\2#g')
local tag="$(curl -sL "https://api.github.com/repos/$repo/releases/latest" | jq -r ".tag_name")"
if [ "$tag" == "null" ]; then
canary="1"
else
local revision=$(curl -sL "https://api.github.com/repos/$repo/compare/$tag...$BUILDKITE_COMMIT" | jq -r ".ahead_by")
if [ "$revision" == "null" ]; then
canary="1"
else
canary="$revision"
fi
fi
run_command buildkite-agent meta-data set canary "$canary"
fi
}
function upload_buildkite_pipeline() {
local path="$1"
if [ ! -f "$path" ]; then
echo "error: Cannot find pipeline: $path"
exit 1
fi
run_command buildkite-agent pipeline upload "$path"
}
function run_command() {
set -x
"$@"
{ set +x; } 2>/dev/null
}
assert_build
assert_buildkite_agent
assert_jq
assert_curl
assert_release
assert_canary
upload_buildkite_pipeline ".buildkite/ci.yml"

View File

@@ -0,0 +1,47 @@
param (
[Parameter(Mandatory=$true)]
[string[]] $Paths,
[switch] $Split
)
$ErrorActionPreference = "Stop"
function Assert-Buildkite-Agent() {
if (-not (Get-Command "buildkite-agent" -ErrorAction SilentlyContinue)) {
Write-Error "Cannot find buildkite-agent, please install it: https://buildkite.com/docs/agent/v3/install"
exit 1
}
}
function Assert-Split-File() {
if (-not (Get-Command "Split-File" -ErrorAction SilentlyContinue)) {
Write-Error "Cannot find Split-File, please install it: https://www.powershellgallery.com/packages/FileSplitter/1.3"
exit 1
}
}
function Upload-Buildkite-Artifact() {
param (
[Parameter(Mandatory=$true)]
[string] $Path,
)
if (-not (Test-Path $Path)) {
Write-Error "Could not find artifact: $Path"
exit 1
}
if ($Split) {
Remove-Item -Path "$Path.*" -Force
Split-File -Path (Resolve-Path $Path) -PartSizeBytes "50MB" -Verbose
$Path = "$Path.*"
}
& buildkite-agent artifact upload "$Path" --debug --debug-http
}
Assert-Buildkite-Agent
if ($Split) {
Assert-Split-File
}
foreach ($Path in $Paths) {
Upload-Buildkite-Artifact $Path
}

View File

@@ -0,0 +1,54 @@
#!/bin/bash
set -eo pipefail
function assert_buildkite_agent() {
if ! command -v buildkite-agent &> /dev/null; then
echo "error: Cannot find buildkite-agent, please install it:"
echo "https://buildkite.com/docs/agent/v3/install"
exit 1
fi
}
function assert_split() {
if ! command -v split &> /dev/null; then
echo "error: Cannot find split, please install it:"
echo "https://www.gnu.org/software/coreutils/split"
exit 1
fi
}
function upload_buildkite_artifact() {
local path="$1"; shift
local split="0"
local args=()
while true; do
if [ -z "$1" ]; then
break
fi
case "$1" in
--split) split="1"; shift ;;
*) args+=("$1"); shift ;;
esac
done
if [ ! -f "$path" ]; then
echo "error: Could not find artifact: $path"
exit 1
fi
if [ "$split" == "1" ]; then
run_command rm -f "$path."*
run_command split -b 50MB -d "$path" "$path."
run_command buildkite-agent artifact upload "$path.*" "${args[@]}"
else
run_command buildkite-agent artifact upload "$path" "${args[@]}"
fi
}
function run_command() {
set -x
"$@"
{ set +x; } 2>/dev/null
}
assert_buildkite_agent
upload_buildkite_artifact "$@"

View File

@@ -3,7 +3,15 @@
set -eo pipefail
function assert_main() {
if [[ "$BUILDKITE_PULL_REQUEST_REPO" && "$BUILDKITE_REPO" != "$BUILDKITE_PULL_REQUEST_REPO" ]]; then
if [ -z "$BUILDKITE_REPO" ]; then
echo "error: Cannot find repository for this build"
exit 1
fi
if [ -z "$BUILDKITE_COMMIT" ]; then
echo "error: Cannot find commit for this build"
exit 1
fi
if [ -n "$BUILDKITE_PULL_REQUEST_REPO" ] && [ "$BUILDKITE_REPO" != "$BUILDKITE_PULL_REQUEST_REPO" ]; then
echo "error: Cannot upload release from a fork"
exit 1
fi
@@ -18,77 +26,191 @@ function assert_main() {
}
function assert_buildkite_agent() {
if ! command -v buildkite-agent &> /dev/null; then
if ! command -v "buildkite-agent" &> /dev/null; then
echo "error: Cannot find buildkite-agent, please install it:"
echo "https://buildkite.com/docs/agent/v3/install"
exit 1
fi
}
function assert_gh() {
if ! command -v gh &> /dev/null; then
echo "warning: gh is not installed, installing..."
function assert_github() {
assert_command "gh" "gh" "https://github.com/cli/cli#installation"
assert_buildkite_secret "GITHUB_TOKEN"
# gh expects the token in $GH_TOKEN
export GH_TOKEN="$GITHUB_TOKEN"
}
function assert_aws() {
assert_command "aws" "awscli" "https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html"
for secret in "AWS_ACCESS_KEY_ID" "AWS_SECRET_ACCESS_KEY" "AWS_ENDPOINT"; do
assert_buildkite_secret "$secret"
done
assert_buildkite_secret "AWS_BUCKET" --skip-redaction
}
function assert_sentry() {
assert_command "sentry-cli" "getsentry/tools/sentry-cli" "https://docs.sentry.io/cli/installation/"
for secret in "SENTRY_AUTH_TOKEN" "SENTRY_ORG" "SENTRY_PROJECT"; do
assert_buildkite_secret "$secret"
done
}
function run_command() {
set -x
"$@"
{ set +x; } 2>/dev/null
}
function assert_command() {
local command="$1"
local package="$2"
local help_url="$3"
if ! command -v "$command" &> /dev/null; then
echo "warning: $command is not installed, installing..."
if command -v brew &> /dev/null; then
brew install gh
HOMEBREW_NO_AUTO_UPDATE=1 run_command brew install "$package"
else
echo "error: Cannot install gh, please install it:"
echo "https://github.com/cli/cli#installation"
echo "error: Cannot install $command, please install it"
if [ -n "$help_url" ]; then
echo ""
echo "hint: See $help_url for help"
fi
exit 1
fi
fi
}
function assert_gh_token() {
local token=$(buildkite-agent secret get GITHUB_TOKEN)
if [ -z "$token" ]; then
echo "error: Cannot find GITHUB_TOKEN secret"
function assert_buildkite_secret() {
local key="$1"
local value=$(buildkite-agent secret get "$key" ${@:2})
if [ -z "$value" ]; then
echo "error: Cannot find $key secret"
echo ""
echo "hint: Create a secret named GITHUB_TOKEN with a GitHub access token:"
echo "hint: Create a secret named $key with a value:"
echo "https://buildkite.com/docs/pipelines/buildkite-secrets"
exit 1
fi
export GH_TOKEN="$token"
export "$key"="$value"
}
function download_artifact() {
local name=$1
buildkite-agent artifact download "$name" .
if [ ! -f "$name" ]; then
function release_tag() {
local version="$1"
if [ "$version" == "canary" ]; then
echo "canary"
else
echo "bun-v$version"
fi
}
function create_sentry_release() {
local version="$1"
local release="$version"
if [ "$version" == "canary" ]; then
release="$BUILDKITE_COMMIT-canary"
fi
run_command sentry-cli releases new "$release" --finalize
run_command sentry-cli releases set-commits "$release" --auto --ignore-missing
if [ "$version" == "canary" ]; then
run_command sentry-cli deploys new --env="canary" --release="$release"
fi
}
function download_buildkite_artifact() {
local name="$1"
local dir="$2"
if [ -z "$dir" ]; then
dir="."
fi
run_command buildkite-agent artifact download "$name" "$dir"
if [ ! -f "$dir/$name" ]; then
echo "error: Cannot find Buildkite artifact: $name"
exit 1
fi
}
function upload_assets() {
local tag=$1
local files=${@:2}
gh release upload "$tag" $files --clobber --repo "$BUILDKITE_REPO"
function upload_github_asset() {
local version="$1"
local tag="$(release_tag "$version")"
local file="$2"
run_command gh release upload "$tag" "$file" --clobber --repo "$BUILDKITE_REPO"
# Sometimes the upload fails, maybe this is a race condition in the gh CLI?
while [ "$(gh release view "$tag" --repo "$BUILDKITE_REPO" | grep -c "$file")" -eq 0 ]; do
echo "warn: Uploading $file to $tag failed, retrying..."
sleep "$((RANDOM % 5 + 1))"
run_command gh release upload "$tag" "$file" --clobber --repo "$BUILDKITE_REPO"
done
}
assert_main
assert_buildkite_agent
assert_gh
assert_gh_token
function update_github_release() {
local version="$1"
local tag="$(release_tag "$version")"
if [ "$tag" == "canary" ]; then
sleep 5 # There is possibly a race condition where this overwrites artifacts?
run_command gh release edit "$tag" --repo "$BUILDKITE_REPO" \
--notes "This release of Bun corresponds to the commit: $BUILDKITE_COMMIT"
fi
}
declare artifacts=(
bun-darwin-aarch64.zip
bun-darwin-aarch64-profile.zip
bun-darwin-x64.zip
bun-darwin-x64-profile.zip
bun-linux-aarch64.zip
bun-linux-aarch64-profile.zip
bun-linux-x64.zip
bun-linux-x64-profile.zip
bun-linux-x64-baseline.zip
bun-linux-x64-baseline-profile.zip
bun-windows-x64.zip
bun-windows-x64-profile.zip
bun-windows-x64-baseline.zip
bun-windows-x64-baseline-profile.zip
)
function upload_s3_file() {
local folder="$1"
local file="$2"
run_command aws --endpoint-url="$AWS_ENDPOINT" s3 cp "$file" "s3://$AWS_BUCKET/$folder/$file"
}
for artifact in "${artifacts[@]}"; do
download_artifact $artifact
done
function create_release() {
assert_main
assert_buildkite_agent
assert_github
assert_aws
assert_sentry
upload_assets "canary" "${artifacts[@]}"
local tag="$1" # 'canary' or 'x.y.z'
local artifacts=(
bun-darwin-aarch64.zip
bun-darwin-aarch64-profile.zip
bun-darwin-x64.zip
bun-darwin-x64-profile.zip
bun-linux-aarch64.zip
bun-linux-aarch64-profile.zip
bun-linux-x64.zip
bun-linux-x64-profile.zip
bun-linux-x64-baseline.zip
bun-linux-x64-baseline-profile.zip
bun-windows-x64.zip
bun-windows-x64-profile.zip
bun-windows-x64-baseline.zip
bun-windows-x64-baseline-profile.zip
)
function upload_artifact() {
local artifact="$1"
download_buildkite_artifact "$artifact"
if [ "$tag" == "canary" ]; then
upload_s3_file "releases/$BUILDKITE_COMMIT-canary" "$artifact" &
else
upload_s3_file "releases/$BUILDKITE_COMMIT" "$artifact" &
fi
upload_s3_file "releases/$tag" "$artifact" &
upload_github_asset "$tag" "$artifact" &
wait
}
for artifact in "${artifacts[@]}"; do
upload_artifact "$artifact"
done
update_github_release "$tag"
create_sentry_release "$tag"
}
function assert_canary() {
local canary="$(buildkite-agent meta-data get canary 2>/dev/null)"
if [ -z "$canary" ] || [ "$canary" == "0" ]; then
echo "warn: Skipping release because this is not a canary build"
exit 0
fi
}
assert_canary
create_release "canary"

View File

@@ -1,312 +0,0 @@
name: Build Darwin
permissions:
contents: read
actions: write
on:
workflow_call:
inputs:
runs-on:
type: string
default: macos-12-large
tag:
type: string
required: true
arch:
type: string
required: true
cpu:
type: string
required: true
assertions:
type: boolean
canary:
type: boolean
no-cache:
type: boolean
env:
LLVM_VERSION: 16
BUN_VERSION: 1.1.8
LC_CTYPE: "en_US.UTF-8"
LC_ALL: "en_US.UTF-8"
jobs:
build-submodules:
name: Build Submodules
runs-on: ${{ inputs.runs-on }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: |
.gitmodules
src/deps
scripts
- name: Hash Submodules
id: hash
run: |
print_versions() {
git submodule | grep -v WebKit
echo "LLVM_VERSION=${{ env.LLVM_VERSION }}"
cat $(echo scripts/build*.sh scripts/all-dependencies.sh | tr " " "\n" | sort)
}
echo "hash=$(print_versions | shasum)" >> $GITHUB_OUTPUT
- if: ${{ !inputs.no-cache }}
name: Restore Cache
id: cache
uses: actions/cache/restore@v4
with:
path: ${{ runner.temp }}/bun-deps
key: bun-${{ inputs.tag }}-deps-${{ steps.hash.outputs.hash }}
# TODO: Figure out how to cache homebrew dependencies
- if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }}
name: Install Dependencies
env:
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1
run: |
brew install \
llvm@${{ env.LLVM_VERSION }} \
ccache \
rust \
pkg-config \
coreutils \
libtool \
cmake \
libiconv \
automake \
openssl@1.1 \
ninja \
golang \
gnu-sed --force --overwrite
echo "$(brew --prefix ccache)/bin" >> $GITHUB_PATH
echo "$(brew --prefix coreutils)/libexec/gnubin" >> $GITHUB_PATH
echo "$(brew --prefix llvm@$LLVM_VERSION)/bin" >> $GITHUB_PATH
brew link --overwrite llvm@$LLVM_VERSION
- if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }}
name: Clone Submodules
run: |
./scripts/update-submodules.sh
- name: Build Submodules
if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }}
env:
CPU_TARGET: ${{ inputs.cpu }}
BUN_DEPS_OUT_DIR: ${{ runner.temp }}/bun-deps
run: |
mkdir -p $BUN_DEPS_OUT_DIR
./scripts/all-dependencies.sh
- name: Save Cache
if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }}
uses: actions/cache/save@v4
with:
path: ${{ runner.temp }}/bun-deps
key: ${{ steps.cache.outputs.cache-primary-key }}
- name: Upload bun-${{ inputs.tag }}-deps
uses: actions/upload-artifact@v4
with:
name: bun-${{ inputs.tag }}-deps
path: ${{ runner.temp }}/bun-deps
if-no-files-found: error
build-cpp:
name: Build C++
runs-on: ${{ inputs.runs-on }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
# TODO: Figure out how to cache homebrew dependencies
- name: Install Dependencies
env:
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1
run: |
brew install \
llvm@${{ env.LLVM_VERSION }} \
ccache \
rust \
pkg-config \
coreutils \
libtool \
cmake \
libiconv \
automake \
openssl@1.1 \
ninja \
golang \
gnu-sed --force --overwrite
echo "$(brew --prefix ccache)/bin" >> $GITHUB_PATH
echo "$(brew --prefix coreutils)/libexec/gnubin" >> $GITHUB_PATH
echo "$(brew --prefix llvm@$LLVM_VERSION)/bin" >> $GITHUB_PATH
brew link --overwrite llvm@$LLVM_VERSION
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: ${{ env.BUN_VERSION }}
- if: ${{ !inputs.no-cache }}
name: Restore Cache
uses: actions/cache@v4
with:
path: ${{ runner.temp }}/ccache
key: bun-${{ inputs.tag }}-cpp-${{ hashFiles('Dockerfile', 'Makefile', 'CMakeLists.txt', 'build.zig', 'scripts/**', 'src/**', 'packages/bun-usockets/src/**', 'packages/bun-uws/src/**') }}
restore-keys: |
bun-${{ inputs.tag }}-cpp-
- name: Compile
env:
CPU_TARGET: ${{ inputs.cpu }}
SOURCE_DIR: ${{ github.workspace }}
OBJ_DIR: ${{ runner.temp }}/bun-cpp-obj
BUN_DEPS_OUT_DIR: ${{ runner.temp }}/bun-deps
CCACHE_DIR: ${{ runner.temp }}/ccache
run: |
mkdir -p $OBJ_DIR
cd $OBJ_DIR
cmake -S $SOURCE_DIR -B $OBJ_DIR \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DUSE_LTO=ON \
-DBUN_CPP_ONLY=1 \
-DNO_CONFIGURE_DEPENDS=1
chmod +x compile-cpp-only.sh
./compile-cpp-only.sh -v
- name: Upload bun-${{ inputs.tag }}-cpp
uses: actions/upload-artifact@v4
with:
name: bun-${{ inputs.tag }}-cpp
path: ${{ runner.temp }}/bun-cpp-obj/bun-cpp-objects.a
if-no-files-found: error
build-zig:
name: Build Zig
uses: ./.github/workflows/build-zig.yml
with:
os: darwin
only-zig: true
tag: ${{ inputs.tag }}
arch: ${{ inputs.arch }}
cpu: ${{ inputs.cpu }}
assertions: ${{ inputs.assertions }}
canary: ${{ inputs.canary }}
no-cache: ${{ inputs.no-cache }}
link:
name: Link
runs-on: ${{ inputs.runs-on }}
needs:
- build-submodules
- build-cpp
- build-zig
steps:
- uses: actions/checkout@v4
# TODO: Figure out how to cache homebrew dependencies
- name: Install Dependencies
env:
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1
run: |
brew install \
llvm@${{ env.LLVM_VERSION }} \
ccache \
rust \
pkg-config \
coreutils \
libtool \
cmake \
libiconv \
automake \
openssl@1.1 \
ninja \
golang \
gnu-sed --force --overwrite
echo "$(brew --prefix ccache)/bin" >> $GITHUB_PATH
echo "$(brew --prefix coreutils)/libexec/gnubin" >> $GITHUB_PATH
echo "$(brew --prefix llvm@$LLVM_VERSION)/bin" >> $GITHUB_PATH
brew link --overwrite llvm@$LLVM_VERSION
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: ${{ env.BUN_VERSION }}
- name: Download bun-${{ inputs.tag }}-deps
uses: actions/download-artifact@v4
with:
name: bun-${{ inputs.tag }}-deps
path: ${{ runner.temp }}/bun-deps
- name: Download bun-${{ inputs.tag }}-cpp
uses: actions/download-artifact@v4
with:
name: bun-${{ inputs.tag }}-cpp
path: ${{ runner.temp }}/bun-cpp-obj
- name: Download bun-${{ inputs.tag }}-zig
uses: actions/download-artifact@v4
with:
name: bun-${{ inputs.tag }}-zig
path: ${{ runner.temp }}/release
- if: ${{ !inputs.no-cache }}
name: Restore Cache
uses: actions/cache@v4
with:
path: ${{ runner.temp }}/ccache
key: bun-${{ inputs.tag }}-cpp-${{ hashFiles('Dockerfile', 'Makefile', 'CMakeLists.txt', 'build.zig', 'scripts/**', 'src/**', 'packages/bun-usockets/src/**', 'packages/bun-uws/src/**') }}
restore-keys: |
bun-${{ inputs.tag }}-cpp-
- name: Link
env:
CPU_TARGET: ${{ inputs.cpu }}
CCACHE_DIR: ${{ runner.temp }}/ccache
run: |
SRC_DIR=$PWD
mkdir ${{ runner.temp }}/link-build
cd ${{ runner.temp }}/link-build
cmake $SRC_DIR \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DUSE_LTO=ON \
-DBUN_LINK_ONLY=1 \
-DBUN_ZIG_OBJ_DIR="${{ runner.temp }}/release" \
-DBUN_CPP_ARCHIVE="${{ runner.temp }}/bun-cpp-obj/bun-cpp-objects.a" \
-DBUN_DEPS_OUT_DIR="${{ runner.temp }}/bun-deps" \
-DNO_CONFIGURE_DEPENDS=1
ninja -v
- name: Prepare
run: |
cd ${{ runner.temp }}/link-build
chmod +x bun-profile bun
mkdir -p bun-${{ inputs.tag }}-profile/ bun-${{ inputs.tag }}/
mv bun-profile bun-${{ inputs.tag }}-profile/bun-profile
mv bun bun-${{ inputs.tag }}/bun
zip -r bun-${{ inputs.tag }}-profile.zip bun-${{ inputs.tag }}-profile
zip -r bun-${{ inputs.tag }}.zip bun-${{ inputs.tag }}
- name: Upload bun-${{ inputs.tag }}
uses: actions/upload-artifact@v4
with:
name: bun-${{ inputs.tag }}
path: ${{ runner.temp }}/link-build/bun-${{ inputs.tag }}.zip
if-no-files-found: error
- name: Upload bun-${{ inputs.tag }}-profile
uses: actions/upload-artifact@v4
with:
name: bun-${{ inputs.tag }}-profile
path: ${{ runner.temp }}/link-build/bun-${{ inputs.tag }}-profile.zip
if-no-files-found: error
on-failure:
if: ${{ github.repository_owner == 'oven-sh' && failure() }}
name: On Failure
needs: link
runs-on: ubuntu-latest
steps:
- name: Send Message
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
nodetail: true
color: "#FF0000"
title: ""
description: |
### ❌ [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})
@${{ github.actor }}, the build for bun-${{ inputs.tag }} failed.
**[View logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})**

View File

@@ -1,64 +0,0 @@
name: Build Linux
permissions:
contents: read
actions: write
on:
workflow_call:
inputs:
runs-on:
type: string
required: true
tag:
type: string
required: true
arch:
type: string
required: true
cpu:
type: string
required: true
assertions:
type: boolean
zig-optimize:
type: string
canary:
type: boolean
no-cache:
type: boolean
jobs:
build:
name: Build Linux
uses: ./.github/workflows/build-zig.yml
with:
os: linux
only-zig: false
runs-on: ${{ inputs.runs-on }}
tag: ${{ inputs.tag }}
arch: ${{ inputs.arch }}
cpu: ${{ inputs.cpu }}
assertions: ${{ inputs.assertions }}
zig-optimize: ${{ inputs.zig-optimize }}
canary: ${{ inputs.canary }}
no-cache: ${{ inputs.no-cache }}
on-failure:
if: ${{ github.repository_owner == 'oven-sh' && failure() }}
name: On Failure
needs: build
runs-on: ubuntu-latest
steps:
- name: Send Message
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
nodetail: true
color: "#FF0000"
title: ""
description: |
### ❌ [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})
@${{ github.actor }}, the build for bun-${{ inputs.tag }} failed.
**[View logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})**

View File

@@ -1,339 +0,0 @@
name: Build Windows
permissions:
contents: read
actions: write
on:
workflow_call:
inputs:
runs-on:
type: string
default: windows
tag:
type: string
required: true
arch:
type: string
required: true
cpu:
type: string
required: true
assertions:
type: boolean
canary:
type: boolean
no-cache:
type: boolean
bun-version:
type: string
default: 1.1.7
env:
# Must specify exact version of LLVM for Windows
LLVM_VERSION: 16.0.6
BUN_VERSION: ${{ inputs.bun-version }}
BUN_GARBAGE_COLLECTOR_LEVEL: 1
BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: 1
CI: true
USE_LTO: 1
jobs:
build-submodules:
name: Build Submodules
runs-on: ${{ inputs.runs-on }}
steps:
- name: Install VS2022 BuildTools 17.9.7
run: choco install -y visualstudio2022buildtools --version=117.9.7.0 --params "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --installChannelUri https://aka.ms/vs/17/release/180911598_-255012421/channel"
- name: Setup Git
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: |
.gitmodules
src/deps
scripts
- name: Hash Submodules
id: hash
run: |
$data = "$(& {
git submodule | Where-Object { $_ -notmatch 'WebKit' }
echo "LLVM_VERSION=${{ env.LLVM_VERSION }}"
Get-Content -Path (Get-ChildItem -Path 'scripts/build*.ps1', 'scripts/all-dependencies.ps1', 'scripts/env.ps1' | Sort-Object -Property Name).FullName | Out-String
echo 1
})"
$hash = ( -join ((New-Object -TypeName System.Security.Cryptography.SHA1CryptoServiceProvider).ComputeHash([System.Text.Encoding]::UTF8.GetBytes($data)) | ForEach-Object { $_.ToString("x2") } )).Substring(0, 10)
echo "hash=${hash}" >> $env:GITHUB_OUTPUT
- if: ${{ !inputs.no-cache }}
name: Restore Cache
id: cache
uses: actions/cache/restore@v4
with:
path: bun-deps
key: bun-${{ inputs.tag }}-deps-${{ steps.hash.outputs.hash }}
- if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }}
name: Install Ninja
run: |
choco install -y ninja
choco install -y llvm --version=${{ env.LLVM_VERSION }} --force
- if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }}
name: Clone Submodules
run: |
.\scripts\update-submodules.ps1
- if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }}
name: Build Dependencies
env:
CPU_TARGET: ${{ inputs.cpu }}
CCACHE_DIR: ccache
USE_LTO: 1
run: |
.\scripts\env.ps1 ${{ contains(inputs.tag, '-baseline') && '-Baseline' || '' }}
choco install -y nasm --version=2.16.01
$env:BUN_DEPS_OUT_DIR = (mkdir -Force "./bun-deps")
.\scripts\all-dependencies.ps1
- name: Save Cache
if: ${{ inputs.no-cache || !steps.cache.outputs.cache-hit }}
uses: actions/cache/save@v4
with:
path: bun-deps
key: ${{ steps.cache.outputs.cache-primary-key }}
- name: Upload bun-${{ inputs.tag }}-deps
uses: actions/upload-artifact@v4
with:
name: bun-${{ inputs.tag }}-deps
path: bun-deps
if-no-files-found: error
codegen:
name: Codegen
runs-on: ubuntu-latest
steps:
- name: Setup Git
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: ${{ inputs.bun-version }}
- name: Codegen
run: |
./scripts/cross-compile-codegen.sh win32 x64
- if: ${{ inputs.canary }}
name: Calculate Revision
run: |
echo "canary_revision=$(GITHUB_TOKEN="${{ github.token }}"
bash ./scripts/calculate-canary-revision.sh --raw)" > build-codegen-win32-x64/.canary_revision
- name: Upload bun-${{ inputs.tag }}-codegen
uses: actions/upload-artifact@v4
with:
name: bun-${{ inputs.tag }}-codegen
path: build-codegen-win32-x64
if-no-files-found: error
build-cpp:
name: Build C++
needs: codegen
runs-on: ${{ inputs.runs-on }}
steps:
- name: Install VS2022 BuildTools 17.9.7
run: choco install -y visualstudio2022buildtools --version=117.9.7.0 --params "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --installChannelUri https://aka.ms/vs/17/release/180911598_-255012421/channel"
- name: Setup Git
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Ninja
run: |
choco install -y ninja
choco install -y llvm --version=${{ env.LLVM_VERSION }} --force
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: ${{ inputs.bun-version }}
- if: ${{ !inputs.no-cache }}
name: Restore Cache
uses: actions/cache@v4
with:
path: ccache
key: bun-${{ inputs.tag }}-cpp-${{ hashFiles('Dockerfile', 'Makefile', 'CMakeLists.txt', 'build.zig', 'scripts/**', 'src/**', 'packages/bun-usockets/src/**', 'packages/bun-uws/src/**') }}
restore-keys: |
bun-${{ inputs.tag }}-cpp-
- name: Download bun-${{ inputs.tag }}-codegen
uses: actions/download-artifact@v4
with:
name: bun-${{ inputs.tag }}-codegen
path: build
- name: Compile
env:
CPU_TARGET: ${{ inputs.cpu }}
CCACHE_DIR: ccache
USE_LTO: 1
run: |
# $CANARY_REVISION = if (Test-Path build/.canary_revision) { Get-Content build/.canary_revision } else { "0" }
$CANARY_REVISION = 0
.\scripts\env.ps1 ${{ contains(inputs.tag, '-baseline') && '-Baseline' || '' }}
.\scripts\update-submodules.ps1
.\scripts\build-libuv.ps1 -CloneOnly $True
cd build
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release `
-DNO_CODEGEN=1 `
-DUSE_LTO=1 `
-DNO_CONFIGURE_DEPENDS=1 `
"-DCANARY=${CANARY_REVISION}" `
-DBUN_CPP_ONLY=1 ${{ contains(inputs.tag, '-baseline') && '-DUSE_BASELINE_BUILD=1' || '' }}
if ($LASTEXITCODE -ne 0) { throw "CMake configuration failed" }
.\compile-cpp-only.ps1 -v
if ($LASTEXITCODE -ne 0) { throw "C++ compilation failed" }
- name: Upload bun-${{ inputs.tag }}-cpp
uses: actions/upload-artifact@v4
with:
name: bun-${{ inputs.tag }}-cpp
path: build/bun-cpp-objects.a
if-no-files-found: error
build-zig:
name: Build Zig
uses: ./.github/workflows/build-zig.yml
with:
os: windows
zig-optimize: ReleaseSafe
only-zig: true
tag: ${{ inputs.tag }}
arch: ${{ inputs.arch }}
cpu: ${{ inputs.cpu }}
assertions: ${{ inputs.assertions }}
canary: ${{ inputs.canary }}
no-cache: ${{ inputs.no-cache }}
link:
name: Link
runs-on: ${{ inputs.runs-on }}
needs:
- build-submodules
- build-cpp
- build-zig
- codegen
steps:
- name: Install VS2022 BuildTools 17.9.7
run: choco install -y visualstudio2022buildtools --version=117.9.7.0 --params "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --installChannelUri https://aka.ms/vs/17/release/180911598_-255012421/channel"
- name: Setup Git
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Ninja
run: |
choco install -y ninja
choco install -y llvm --version=${{ env.LLVM_VERSION }} --force
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: ${{ inputs.bun-version }}
- name: Download bun-${{ inputs.tag }}-deps
uses: actions/download-artifact@v4
with:
name: bun-${{ inputs.tag }}-deps
path: bun-deps
- name: Download bun-${{ inputs.tag }}-cpp
uses: actions/download-artifact@v4
with:
name: bun-${{ inputs.tag }}-cpp
path: bun-cpp
- name: Download bun-${{ inputs.tag }}-zig
uses: actions/download-artifact@v4
with:
name: bun-${{ inputs.tag }}-zig
path: bun-zig
- name: Download bun-${{ inputs.tag }}-codegen
uses: actions/download-artifact@v4
with:
name: bun-${{ inputs.tag }}-codegen
path: build
- if: ${{ !inputs.no-cache }}
name: Restore Cache
uses: actions/cache@v4
with:
path: ccache
key: bun-${{ inputs.tag }}-cpp-${{ hashFiles('Dockerfile', 'Makefile', 'CMakeLists.txt', 'build.zig', 'scripts/**', 'src/**', 'packages/bun-usockets/src/**', 'packages/bun-uws/src/**') }}
restore-keys: |
bun-${{ inputs.tag }}-cpp-
- name: Link
env:
CPU_TARGET: ${{ inputs.cpu }}
CCACHE_DIR: ccache
run: |
.\scripts\update-submodules.ps1
.\scripts\env.ps1 ${{ contains(inputs.tag, '-baseline') && '-Baseline' || '' }}
Set-Location build
# $CANARY_REVISION = if (Test-Path build/.canary_revision) { Get-Content build/.canary_revision } else { "0" }
$CANARY_REVISION = 0
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release `
-DNO_CODEGEN=1 `
-DNO_CONFIGURE_DEPENDS=1 `
"-DCANARY=${CANARY_REVISION}" `
-DBUN_LINK_ONLY=1 `
-DUSE_LTO=1 `
"-DBUN_DEPS_OUT_DIR=$(Resolve-Path ../bun-deps)" `
"-DBUN_CPP_ARCHIVE=$(Resolve-Path ../bun-cpp/bun-cpp-objects.a)" `
"-DBUN_ZIG_OBJ_DIR=$(Resolve-Path ../bun-zig)" `
${{ contains(inputs.tag, '-baseline') && '-DUSE_BASELINE_BUILD=1' || '' }}
if ($LASTEXITCODE -ne 0) { throw "CMake configuration failed" }
ninja -v
if ($LASTEXITCODE -ne 0) { throw "Link failed!" }
- name: Prepare
run: |
$Dist = mkdir -Force "bun-${{ inputs.tag }}"
cp -r build\bun.exe "$Dist\bun.exe"
Compress-Archive -Force "$Dist" "${Dist}.zip"
$Dist = "$Dist-profile"
MkDir -Force "$Dist"
cp -r build\bun.exe "$Dist\bun.exe"
cp -r build\bun.pdb "$Dist\bun.pdb"
Compress-Archive -Force "$Dist" "$Dist.zip"
.\build\bun.exe --print "JSON.stringify(require('bun:internal-for-testing').crash_handler.getFeatureData())" > .\features.json
- name: Upload bun-${{ inputs.tag }}
uses: actions/upload-artifact@v4
with:
name: bun-${{ inputs.tag }}
path: bun-${{ inputs.tag }}.zip
if-no-files-found: error
- name: Upload bun-${{ inputs.tag }}-profile
uses: actions/upload-artifact@v4
with:
name: bun-${{ inputs.tag }}-profile
path: bun-${{ inputs.tag }}-profile.zip
if-no-files-found: error
- name: Upload bun-feature-data
uses: actions/upload-artifact@v4
with:
name: bun-feature-data
path: features.json
if-no-files-found: error
overwrite: true
on-failure:
if: ${{ github.repository_owner == 'oven-sh' && failure() }}
name: On Failure
needs: link
runs-on: ubuntu-latest
steps:
- name: Send Message
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
nodetail: true
color: "#FF0000"
title: ""
description: |
### ❌ [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})
@${{ github.actor }}, the build for bun-${{ inputs.tag }} failed.
**[View logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})**

View File

@@ -1,122 +0,0 @@
name: Build Zig
permissions:
contents: read
actions: write
on:
workflow_call:
inputs:
runs-on:
type: string
default: ${{ github.repository_owner != 'oven-sh' && 'ubuntu-latest' || inputs.only-zig && 'namespace-profile-bun-ci-linux-x64' || inputs.arch == 'x64' && 'namespace-profile-bun-ci-linux-x64' || 'namespace-profile-bun-ci-linux-aarch64' }}
tag:
type: string
required: true
os:
type: string
required: true
arch:
type: string
required: true
cpu:
type: string
required: true
assertions:
type: boolean
default: false
zig-optimize:
type: string # 'ReleaseSafe' or 'ReleaseFast'
default: ReleaseFast
canary:
type: boolean
default: ${{ github.ref == 'refs/heads/main' }}
only-zig:
type: boolean
default: true
no-cache:
type: boolean
default: false
jobs:
build-zig:
name: ${{ inputs.only-zig && 'Build Zig' || 'Build & Link' }}
runs-on: ${{ inputs.runs-on }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Calculate Cache Key
id: cache
run: |
echo "key=${{ hashFiles('Dockerfile', 'Makefile', 'CMakeLists.txt', 'build.zig', 'scripts/**', 'src/**', 'packages/bun-usockets/src/**', 'packages/bun-uws/src/**') }}" >> $GITHUB_OUTPUT
- if: ${{ !inputs.no-cache }}
name: Restore Cache
uses: actions/cache@v4
with:
key: bun-${{ inputs.tag }}-docker-${{ steps.cache.outputs.key }}
restore-keys: |
bun-${{ inputs.tag }}-docker-
path: |
${{ runner.temp }}/dockercache
- name: Setup Docker
uses: docker/setup-buildx-action@v3
with:
install: true
platforms: |
linux/${{ runner.arch == 'X64' && 'amd64' || 'arm64' }}
- name: Build
uses: docker/build-push-action@v5
with:
push: false
target: ${{ inputs.only-zig && 'build_release_obj' || 'artifact' }}
cache-from: |
type=local,src=${{ runner.temp }}/dockercache
cache-to: |
type=local,dest=${{ runner.temp }}/dockercache,mode=max
outputs: |
type=local,dest=${{ runner.temp }}/release
platforms: |
linux/${{ runner.arch == 'X64' && 'amd64' || 'arm64' }}
build-args: |
GIT_SHA=${{ github.event.workflow_run.head_sha || github.sha }}
TRIPLET=${{ inputs.os == 'darwin' && format('{0}-macos-none', inputs.arch == 'x64' && 'x86_64' || 'aarch64') || inputs.os == 'windows' && format('{0}-windows-msvc', inputs.arch == 'x64' && 'x86_64' || 'aarch64') || format('{0}-linux-gnu', inputs.arch == 'x64' && 'x86_64' || 'aarch64') }}
ARCH=${{ inputs.arch == 'x64' && 'x86_64' || 'aarch64' }}
BUILDARCH=${{ inputs.arch == 'x64' && 'amd64' || 'arm64' }}
BUILD_MACHINE_ARCH=${{ inputs.arch == 'x64' && 'x86_64' || 'aarch64' }}
CPU_TARGET=${{ inputs.arch == 'x64' && inputs.cpu || 'native' }}
ASSERTIONS=${{ inputs.assertions && 'ON' || 'OFF' }}
ZIG_OPTIMIZE=${{ inputs.zig-optimize }}
CANARY=${{ inputs.canary && '1' || '0' }}
- if: ${{ inputs.only-zig }}
name: Upload bun-${{ inputs.tag }}-zig
uses: actions/upload-artifact@v4
with:
name: bun-${{ inputs.tag }}-zig
path: ${{ runner.temp }}/release/bun-zig.o
if-no-files-found: error
- if: ${{ !inputs.only-zig }}
name: Prepare
run: |
cd ${{ runner.temp }}/release
chmod +x bun-profile bun
mkdir bun-${{ inputs.tag }}-profile
mkdir bun-${{ inputs.tag }}
strip bun
mv bun-profile bun-${{ inputs.tag }}-profile/bun-profile
mv bun bun-${{ inputs.tag }}/bun
zip -r bun-${{ inputs.tag }}-profile.zip bun-${{ inputs.tag }}-profile
zip -r bun-${{ inputs.tag }}.zip bun-${{ inputs.tag }}
- if: ${{ !inputs.only-zig }}
name: Upload bun-${{ inputs.tag }}
uses: actions/upload-artifact@v4
with:
name: bun-${{ inputs.tag }}
path: ${{ runner.temp }}/release/bun-${{ inputs.tag }}.zip
if-no-files-found: error
- if: ${{ !inputs.only-zig }}
name: Upload bun-${{ inputs.tag }}-profile
uses: actions/upload-artifact@v4
with:
name: bun-${{ inputs.tag }}-profile
path: ${{ runner.temp }}/release/bun-${{ inputs.tag }}-profile.zip
if-no-files-found: error

View File

@@ -1,245 +0,0 @@
name: CI
permissions:
contents: read
actions: write
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && inputs.run-id || github.ref }}
cancel-in-progress: true
on:
workflow_dispatch:
inputs:
run-id:
type: string
description: The workflow ID to download artifacts (skips the build step)
pull_request:
paths-ignore:
- .vscode/**/*
- docs/**/*
- examples/**/*
push:
branches:
- main
paths-ignore:
- .vscode/**/*
- docs/**/*
- examples/**/*
jobs:
format:
if: ${{ !inputs.run-id }}
name: Format
uses: ./.github/workflows/run-format.yml
secrets: inherit
with:
zig-version: 0.13.0
permissions:
contents: write
lint:
if: ${{ !inputs.run-id }}
name: Lint
uses: ./.github/workflows/run-lint.yml
secrets: inherit
linux-x64:
if: ${{ !inputs.run-id }}
name: Build linux-x64
uses: ./.github/workflows/build-linux.yml
secrets: inherit
with:
runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-linux-x64' || 'ubuntu-latest' }}
tag: linux-x64
arch: x64
cpu: haswell
canary: true
no-cache: true
linux-x64-baseline:
if: ${{ !inputs.run-id }}
name: Build linux-x64-baseline
uses: ./.github/workflows/build-linux.yml
secrets: inherit
with:
runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-linux-x64' || 'ubuntu-latest' }}
tag: linux-x64-baseline
arch: x64
cpu: nehalem
canary: true
no-cache: true
linux-aarch64:
if: ${{ !inputs.run-id && github.repository_owner == 'oven-sh' }}
name: Build linux-aarch64
uses: ./.github/workflows/build-linux.yml
secrets: inherit
with:
runs-on: namespace-profile-bun-ci-linux-aarch64
tag: linux-aarch64
arch: aarch64
cpu: native
canary: true
no-cache: true
darwin-x64:
if: ${{ !inputs.run-id }}
name: Build darwin-x64
uses: ./.github/workflows/build-darwin.yml
secrets: inherit
with:
runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-12-large' || 'macos-12' }}
tag: darwin-x64
arch: x64
cpu: haswell
canary: true
darwin-x64-baseline:
if: ${{ !inputs.run-id }}
name: Build darwin-x64-baseline
uses: ./.github/workflows/build-darwin.yml
secrets: inherit
with:
runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-12-large' || 'macos-12' }}
tag: darwin-x64-baseline
arch: x64
cpu: nehalem
canary: true
darwin-aarch64:
if: ${{ !inputs.run-id }}
name: Build darwin-aarch64
uses: ./.github/workflows/build-darwin.yml
secrets: inherit
with:
runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-darwin-aarch64' || 'macos-12' }}
tag: darwin-aarch64
arch: aarch64
cpu: native
canary: true
windows-x64:
if: ${{ !inputs.run-id }}
name: Build windows-x64
uses: ./.github/workflows/build-windows.yml
secrets: inherit
with:
runs-on: windows
tag: windows-x64
arch: x64
cpu: haswell
canary: true
windows-x64-baseline:
if: ${{ !inputs.run-id }}
name: Build windows-x64-baseline
uses: ./.github/workflows/build-windows.yml
secrets: inherit
with:
runs-on: windows
tag: windows-x64-baseline
arch: x64
cpu: nehalem
canary: true
linux-x64-test:
if: ${{ inputs.run-id || github.event_name == 'pull_request' }}
name: Test linux-x64
needs: linux-x64
uses: ./.github/workflows/run-test.yml
secrets: inherit
with:
run-id: ${{ inputs.run-id }}
pr-number: ${{ github.event.number }}
runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-linux-x64' || 'ubuntu-latest' }}
tag: linux-x64
linux-x64-baseline-test:
if: ${{ inputs.run-id || github.event_name == 'pull_request' }}
name: Test linux-x64-baseline
needs: linux-x64-baseline
uses: ./.github/workflows/run-test.yml
secrets: inherit
with:
run-id: ${{ inputs.run-id }}
pr-number: ${{ github.event.number }}
runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-linux-x64' || 'ubuntu-latest' }}
tag: linux-x64-baseline
linux-aarch64-test:
if: ${{ inputs.run-id || github.event_name == 'pull_request' && github.repository_owner == 'oven-sh'}}
name: Test linux-aarch64
needs: linux-aarch64
uses: ./.github/workflows/run-test.yml
secrets: inherit
with:
run-id: ${{ inputs.run-id }}
pr-number: ${{ github.event.number }}
runs-on: namespace-profile-bun-ci-linux-aarch64
tag: linux-aarch64
darwin-x64-test:
if: ${{ inputs.run-id || github.event_name == 'pull_request' }}
name: Test darwin-x64
needs: darwin-x64
uses: ./.github/workflows/run-test.yml
secrets: inherit
with:
run-id: ${{ inputs.run-id }}
pr-number: ${{ github.event.number }}
runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-12-large' || 'macos-12' }}
tag: darwin-x64
darwin-x64-baseline-test:
if: ${{ inputs.run-id || github.event_name == 'pull_request' }}
name: Test darwin-x64-baseline
needs: darwin-x64-baseline
uses: ./.github/workflows/run-test.yml
secrets: inherit
with:
run-id: ${{ inputs.run-id }}
pr-number: ${{ github.event.number }}
runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-12-large' || 'macos-12' }}
tag: darwin-x64-baseline
darwin-aarch64-test:
if: ${{ inputs.run-id || github.event_name == 'pull_request' }}
name: Test darwin-aarch64
needs: darwin-aarch64
uses: ./.github/workflows/run-test.yml
secrets: inherit
with:
run-id: ${{ inputs.run-id }}
pr-number: ${{ github.event.number }}
runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-darwin-aarch64' || 'macos-12' }}
tag: darwin-aarch64
windows-x64-test:
if: ${{ inputs.run-id || github.event_name == 'pull_request' }}
name: Test windows-x64
needs: windows-x64
uses: ./.github/workflows/run-test.yml
secrets: inherit
with:
run-id: ${{ inputs.run-id }}
pr-number: ${{ github.event.number }}
runs-on: windows
tag: windows-x64
windows-x64-baseline-test:
if: ${{ inputs.run-id || github.event_name == 'pull_request' }}
name: Test windows-x64-baseline
needs: windows-x64-baseline
uses: ./.github/workflows/run-test.yml
secrets: inherit
with:
run-id: ${{ inputs.run-id }}
pr-number: ${{ github.event.number }}
runs-on: windows
tag: windows-x64-baseline
cleanup:
if: ${{ always() }}
name: Cleanup
needs:
- linux-x64
- linux-x64-baseline
- linux-aarch64
- darwin-x64
- darwin-x64-baseline
- darwin-aarch64
- windows-x64
- windows-x64-baseline
runs-on: ubuntu-latest
steps:
- name: Cleanup Artifacts
uses: geekyeggo/delete-artifact@v5
with:
name: |
bun-*-cpp
bun-*-zig
bun-*-deps
bun-*-codegen

View File

@@ -1,55 +0,0 @@
name: Comment
permissions:
actions: read
pull-requests: write
on:
workflow_run:
workflows:
- CI
types:
- completed
jobs:
comment:
if: ${{ github.repository_owner == 'oven-sh' }}
name: Comment
runs-on: ubuntu-latest
steps:
- name: Download Tests
uses: actions/download-artifact@v4
with:
path: bun
pattern: bun-*-tests
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
- name: Setup Environment
id: env
shell: bash
run: |
echo "pr-number=$(<bun/bun-linux-x64-tests/pr-number.txt)" >> $GITHUB_OUTPUT
- name: Generate Comment
run: |
cat bun/bun-*-tests/comment.md > comment.md
if [ -s comment.md ]; then
echo -e "❌ @${{ github.actor }}, your commit has failing tests :(\n\n$(cat comment.md)" > comment.md
else
echo -e "✅ @${{ github.actor }}, all tests passed!" > comment.md
fi
echo -e "\n**[View logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }})**" >> comment.md
echo -e "<!-- generated-comment workflow=${{ github.workflow }} -->" >> comment.md
- name: Find Comment
id: comment
uses: peter-evans/find-comment@v3
with:
issue-number: ${{ steps.env.outputs.pr-number }}
comment-author: github-actions[bot]
body-includes: <!-- generated-comment workflow=${{ github.workflow }} -->
- name: Write Comment
uses: peter-evans/create-or-update-comment@v4
with:
comment-id: ${{ steps.comment.outputs.comment-id }}
issue-number: ${{ steps.env.outputs.pr-number }}
body-path: comment.md
edit-mode: replace

View File

@@ -1,183 +0,0 @@
name: Create Release Build
run-name: Compile Bun v${{ inputs.version }} by ${{ github.actor }}
concurrency:
group: release
cancel-in-progress: true
permissions:
contents: write
actions: write
on:
workflow_dispatch:
inputs:
version:
type: string
required: true
description: "Release version. Example: 1.1.4. Exclude the 'v' prefix."
tag:
type: string
required: true
description: "GitHub tag to use"
clobber:
type: boolean
required: false
default: false
description: "Overwrite existing release artifacts?"
release:
types:
- created
jobs:
notify-start:
if: ${{ github.repository_owner == 'oven-sh' }}
name: Notify Start
runs-on: ubuntu-latest
steps:
- name: Send Message
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.DISCORD_WEBHOOK_PUBLIC }}
nodetail: true
color: "#1F6FEB"
title: "Bun v${{ inputs.version }} is compiling"
description: |
### @${{ github.actor }} started compiling Bun v${{inputs.version}}
- name: Send Message
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.BUN_DISCORD_GITHUB_CHANNEL_WEBHOOK }}
nodetail: true
color: "#1F6FEB"
title: "Bun v${{ inputs.version }} is compiling"
description: |
### @${{ github.actor }} started compiling Bun v${{inputs.version}}
**[View logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})**
linux-x64:
name: Build linux-x64
uses: ./.github/workflows/build-linux.yml
secrets: inherit
with:
runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-linux-x64' || 'ubuntu-latest' }}
tag: linux-x64
arch: x64
cpu: haswell
canary: false
linux-x64-baseline:
name: Build linux-x64-baseline
uses: ./.github/workflows/build-linux.yml
secrets: inherit
with:
runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-linux-x64' || 'ubuntu-latest' }}
tag: linux-x64-baseline
arch: x64
cpu: nehalem
canary: false
linux-aarch64:
name: Build linux-aarch64
uses: ./.github/workflows/build-linux.yml
secrets: inherit
with:
runs-on: namespace-profile-bun-ci-linux-aarch64
tag: linux-aarch64
arch: aarch64
cpu: native
canary: false
darwin-x64:
name: Build darwin-x64
uses: ./.github/workflows/build-darwin.yml
secrets: inherit
with:
runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-12-large' || 'macos-12' }}
tag: darwin-x64
arch: x64
cpu: haswell
canary: false
darwin-x64-baseline:
name: Build darwin-x64-baseline
uses: ./.github/workflows/build-darwin.yml
secrets: inherit
with:
runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-12-large' || 'macos-12' }}
tag: darwin-x64-baseline
arch: x64
cpu: nehalem
canary: false
darwin-aarch64:
name: Build darwin-aarch64
uses: ./.github/workflows/build-darwin.yml
secrets: inherit
with:
runs-on: ${{ github.repository_owner == 'oven-sh' && 'namespace-profile-bun-ci-darwin-aarch64' || 'macos-12' }}
tag: darwin-aarch64
arch: aarch64
cpu: native
canary: false
windows-x64:
name: Build windows-x64
uses: ./.github/workflows/build-windows.yml
secrets: inherit
with:
runs-on: windows
tag: windows-x64
arch: x64
cpu: haswell
canary: false
windows-x64-baseline:
name: Build windows-x64-baseline
uses: ./.github/workflows/build-windows.yml
secrets: inherit
with:
runs-on: windows
tag: windows-x64-baseline
arch: x64
cpu: nehalem
canary: false
upload-artifacts:
needs:
- linux-x64
- linux-x64-baseline
- linux-aarch64
- darwin-x64
- darwin-x64-baseline
- darwin-aarch64
- windows-x64
- windows-x64-baseline
runs-on: ubuntu-latest
steps:
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
path: bun-releases
pattern: bun-*
merge-multiple: true
github-token: ${{ github.token }}
- name: Check for Artifacts
run: |
if [ ! -d "bun-releases" ] || [ -z "$(ls -A bun-releases)" ]; then
echo "Error: No artifacts were downloaded or 'bun-releases' directory does not exist."
exit 1 # Fail the job if the condition is met
else
echo "Artifacts downloaded successfully."
fi
- name: Send Message
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
nodetail: true
color: "#FF0000"
title: "Bun v${{ inputs.version }} release artifacts uploaded"
- name: "Upload Artifacts"
env:
GH_TOKEN: ${{ github.token }}
run: |
# Unzip one level deep each artifact
cd bun-releases
for f in *.zip; do
unzip -o $f
done
cd ..
gh release upload --repo=${{ github.repository }} ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.event.release.id }} ${{ inputs.clobber && '--clobber' || '' }} bun-releases/*.zip

View File

@@ -1,3 +1,6 @@
# TODO: Move this to bash scripts intead of Github Actions
# so it can be run from Buildkite, see: .buildkite/scripts/release.sh
name: Release
concurrency: release
@@ -63,7 +66,7 @@ jobs:
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: "1.0.21"
bun-version: "1.1.20"
- name: Install Dependencies
run: bun install
- name: Sign Release
@@ -88,7 +91,7 @@ jobs:
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: "1.0.21"
bun-version: "1.1.20"
- name: Install Dependencies
run: bun install
- name: Release
@@ -117,7 +120,7 @@ jobs:
if: ${{ env.BUN_VERSION != 'canary' }}
uses: ./.github/actions/setup-bun
with:
bun-version: "1.0.21"
bun-version: "1.1.20"
- name: Setup Bun
if: ${{ env.BUN_VERSION == 'canary' }}
uses: ./.github/actions/setup-bun
@@ -259,7 +262,7 @@ jobs:
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: "1.0.21"
bun-version: "1.1.20"
- name: Install Dependencies
run: bun install
- name: Release
@@ -270,6 +273,24 @@ jobs:
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
AWS_ENDPOINT: ${{ secrets.AWS_ENDPOINT }}
AWS_BUCKET: bun
notify-sentry:
name: Notify Sentry
runs-on: ubuntu-latest
needs: s3
steps:
- name: Notify Sentry
uses: getsentry/action-release@v1.7.0
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
with:
ignore_missing: true
ignore_empty: true
version: ${{ env.BUN_VERSION }}
environment: production
bump:
name: "Bump version"
runs-on: ubuntu-latest

View File

@@ -29,9 +29,9 @@ jobs:
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: "1.1.8"
bun-version: "1.1.20"
- name: Setup Zig
uses: goto-bus-stop/setup-zig@c7b6cdd3adba8f8b96984640ff172c37c93f73ee
uses: mlugg/setup-zig@v1
with:
version: ${{ inputs.zig-version }}
- name: Install Dependencies

View File

@@ -3,7 +3,7 @@ name: lint-cpp
permissions:
contents: read
env:
LLVM_VERSION: 16
LLVM_VERSION: 18
LC_CTYPE: "en_US.UTF-8"
LC_ALL: "en_US.UTF-8"
@@ -17,7 +17,7 @@ on:
jobs:
lint-cpp:
name: Lint C++
runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-13-xlarge' || 'macos-12' }}
runs-on: ${{ github.repository_owner == 'oven-sh' && 'macos-13-xlarge' || 'macos-13' }}
steps:
- name: Checkout
uses: actions/checkout@v4

View File

@@ -1,224 +0,0 @@
name: Test
permissions:
contents: read
actions: read
on:
workflow_call:
inputs:
runs-on:
type: string
required: true
tag:
type: string
required: true
pr-number:
type: string
required: true
run-id:
type: string
default: ${{ github.run_id }}
jobs:
test:
name: Tests
runs-on: ${{ inputs.runs-on }}
steps:
- if: ${{ runner.os == 'Windows' }}
name: Setup Git
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: |
package.json
bun.lockb
test
packages/bun-internal-test
packages/bun-types
- name: Setup Environment
shell: bash
run: |
echo "${{ inputs.pr-number }}" > pr-number.txt
- name: Download Bun
uses: actions/download-artifact@v4
with:
name: bun-${{ inputs.tag }}
path: bun
github-token: ${{ github.token }}
run-id: ${{ inputs.run-id || github.run_id }}
- name: Download pnpm
uses: pnpm/action-setup@v4
with:
version: 8
- if: ${{ runner.os != 'Windows' }}
name: Setup Bun
shell: bash
run: |
unzip bun/bun-*.zip
cd bun-*
pwd >> $GITHUB_PATH
- if: ${{ runner.os == 'Windows' }}
name: Setup Cygwin
uses: secondlife/setup-cygwin@v3
with:
packages: bash
- if: ${{ runner.os == 'Windows' }}
name: Setup Bun (Windows)
run: |
unzip bun/bun-*.zip
cd bun-*
pwd >> $env:GITHUB_PATH
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install Dependencies
timeout-minutes: 5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
bun install
- name: Install Dependencies (test)
timeout-minutes: 5
run: |
bun install --cwd test
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install Dependencies (runner)
timeout-minutes: 5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
bun install --cwd packages/bun-internal-test
- name: Run Tests
id: test
timeout-minutes: 90
shell: bash
env:
IS_BUN_CI: 1
TMPDIR: ${{ runner.temp }}
BUN_TAG: ${{ inputs.tag }}
BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: "true"
SMTP_SENDGRID_SENDER: ${{ secrets.SMTP_SENDGRID_SENDER }}
TLS_MONGODB_DATABASE_URL: ${{ secrets.TLS_MONGODB_DATABASE_URL }}
TLS_POSTGRES_DATABASE_URL: ${{ secrets.TLS_POSTGRES_DATABASE_URL }}
TEST_INFO_STRIPE: ${{ secrets.TEST_INFO_STRIPE }}
TEST_INFO_AZURE_SERVICE_BUS: ${{ secrets.TEST_INFO_AZURE_SERVICE_BUS }}
SHELLOPTS: igncr
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
node packages/bun-internal-test/src/runner.node.mjs $(which bun)
- if: ${{ always() }}
name: Upload Results
uses: actions/upload-artifact@v4
with:
name: bun-${{ inputs.tag }}-tests
path: |
test-report.*
comment.md
pr-number.txt
if-no-files-found: error
overwrite: true
- if: ${{ always() && steps.test.outputs.failing_tests != '' && github.event.pull_request && github.repository_owner == 'oven-sh' }}
name: Send Message
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
nodetail: true
color: "#FF0000"
title: ""
description: |
### ❌ [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})
@${{ github.actor }}, there are ${{ steps.test.outputs.failing_tests_count || 'some' }} failing tests on bun-${{ inputs.tag }}.
${{ steps.test.outputs.failing_tests }}
**[View logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})**
- name: Fail
if: ${{ failure() || always() && steps.test.outputs.failing_tests != '' }}
run: |
echo "There are ${{ steps.test.outputs.failing_tests_count || 'some' }} failing tests on bun-${{ inputs.tag }}."
exit 1
test-node:
name: Node.js Tests
# TODO: enable when we start paying attention to the results. In the meantime, this causes CI to queue jobs wasting developer time.
if: 0
runs-on: ${{ inputs.runs-on }}
steps:
- if: ${{ runner.os == 'Windows' }}
name: Setup Git
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: |
test/node.js
- name: Setup Environment
shell: bash
run: |
echo "${{ inputs.pr-number }}" > pr-number.txt
- name: Download Bun
uses: actions/download-artifact@v4
with:
name: bun-${{ inputs.tag }}
path: bun
github-token: ${{ github.token }}
run-id: ${{ inputs.run-id || github.run_id }}
- if: ${{ runner.os != 'Windows' }}
name: Setup Bun
shell: bash
run: |
unzip bun/bun-*.zip
cd bun-*
pwd >> $GITHUB_PATH
- if: ${{ runner.os == 'Windows' }}
name: Setup Cygwin
uses: secondlife/setup-cygwin@v3
with:
packages: bash
- if: ${{ runner.os == 'Windows' }}
name: Setup Bun (Windows)
run: |
unzip bun/bun-*.zip
cd bun-*
pwd >> $env:GITHUB_PATH
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Checkout Tests
shell: bash
working-directory: test/node.js
run: |
node runner.mjs --pull
- name: Install Dependencies
timeout-minutes: 5
shell: bash
working-directory: test/node.js
run: |
bun install
- name: Run Tests
timeout-minutes: 10 # Increase when more tests are added
shell: bash
working-directory: test/node.js
env:
TMPDIR: ${{ runner.temp }}
BUN_GARBAGE_COLLECTOR_LEVEL: "0"
BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: "true"
run: |
node runner.mjs
- name: Upload Results
uses: actions/upload-artifact@v4
with:
name: bun-${{ inputs.tag }}-node-tests
path: |
test/node.js/summary/*.json
if-no-files-found: error
overwrite: true

View File

@@ -1,82 +0,0 @@
name: Upload Artifacts
run-name: Canary release ${{github.sha}} upload
permissions:
contents: write
on:
workflow_run:
workflows:
- CI
types:
- completed
branches:
- main
jobs:
upload:
if: ${{ github.repository_owner == 'oven-sh' }}
name: Upload Artifacts
runs-on: ubuntu-latest
steps:
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
path: bun
pattern: bun-*
merge-multiple: true
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
- name: Check for Artifacts
run: |
if [ ! -d "bun" ] || [ -z "$(ls -A bun)" ]; then
echo "Error: No artifacts were downloaded or 'bun' directory does not exist."
exit 1 # Fail the job if the condition is met
else
echo "Artifacts downloaded successfully."
fi
- name: Upload to GitHub Releases
uses: ncipollo/release-action@v1
with:
tag: canary
name: Canary (${{ github.sha }})
prerelease: true
body: This canary release of Bun corresponds to the commit [${{ github.sha }}]
allowUpdates: true
replacesArtifacts: true
generateReleaseNotes: true
artifactErrorsFailBuild: true
artifacts: bun/**/bun-*.zip
token: ${{ github.token }}
- name: Upload to S3 (using SHA)
uses: shallwefootball/s3-upload-action@4350529f410221787ccf424e50133cbc1b52704e
with:
endpoint: ${{ secrets.AWS_ENDPOINT }}
aws_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
aws_bucket: ${{ secrets.AWS_BUCKET }}
source_dir: bun
destination_dir: releases/${{ github.event.workflow_run.head_sha || github.sha }}-canary
- name: Upload to S3 (using tag)
uses: shallwefootball/s3-upload-action@4350529f410221787ccf424e50133cbc1b52704e
with:
endpoint: ${{ secrets.AWS_ENDPOINT }}
aws_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
aws_bucket: ${{ secrets.AWS_BUCKET }}
source_dir: bun
destination_dir: releases/canary
- name: Announce on Discord
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.BUN_DISCORD_GITHUB_CHANNEL_WEBHOOK }}
nodetail: true
color: "#1F6FEB"
title: "New Bun Canary available"
url: https://github.com/oven-sh/bun/commit/${{ github.sha }}
description: |
A new canary build of Bun has been automatically uploaded. To upgrade, run:
```sh
bun upgrade --canary
# bun upgrade --stable <- to downgrade
```

1
.gitignore vendored
View File

@@ -145,3 +145,4 @@ zig-cache
zig-out
test/node.js/upstream
.zig-cache
scripts/env.local

4
.gitmodules vendored
View File

@@ -82,3 +82,7 @@ url = https://github.com/oven-sh/zig
depth = 1
shallow = true
fetchRecurseSubmodules = false
[submodule "src/deps/libdeflate"]
path = src/deps/libdeflate
url = https://github.com/ebiggers/libdeflate
ignore = "dirty"

4
.lldbinit Normal file
View File

@@ -0,0 +1,4 @@
command script import src/deps/zig/tools/lldb_pretty_printers.py
command script import src/bun.js/WebKit/Tools/lldb/lldb_webkit.py
# type summary add --summary-string "${var} | inner=${var[0-30]}, source=${var[33-64]}, tag=${var[31-32]}" "unsigned long"

1
.vscode/launch.json generated vendored
View File

@@ -17,6 +17,7 @@
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "1",
},
"console": "internalConsole",

View File

@@ -42,8 +42,11 @@
"editor.defaultFormatter": "ziglang.vscode-zig",
},
// C++
// lldb
"lldb.launch.initCommands": ["command source ${workspaceFolder}/.lldbinit"],
"lldb.verboseLogging": false,
// C++
"cmake.configureOnOpen": false,
"C_Cpp.errorSquiggles": "enabled",
"[cpp]": {

View File

@@ -3,8 +3,8 @@ cmake_policy(SET CMP0091 NEW)
cmake_policy(SET CMP0067 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
set(Bun_VERSION "1.1.19")
set(WEBKIT_TAG 615e8585f96aa718b0f5158210259b83fe8440ea)
set(Bun_VERSION "1.1.22")
set(WEBKIT_TAG a060f087c2232fb20d82c321d21e074e735d3261)
set(BUN_WORKDIR "${CMAKE_CURRENT_BINARY_DIR}")
message(STATUS "Configuring Bun ${Bun_VERSION} in ${BUN_WORKDIR}")
@@ -15,7 +15,19 @@ set(CMAKE_C_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD_REQUIRED ON)
# Should not start with v
option(ZIG_CACHE_DIR "Path to the Zig cache directory" "")
if(NOT ZIG_CACHE_DIR)
SET(ZIG_CACHE_DIR "${BUN_WORKDIR}")
cmake_path(APPEND ZIG_CACHE_DIR "zig-cache")
endif()
set(LOCAL_ZIG_CACHE_DIR "${ZIG_CACHE_DIR}")
set(GLOBAL_ZIG_CACHE_DIR "${ZIG_CACHE_DIR}")
cmake_path(APPEND LOCAL_ZIG_CACHE_DIR "local")
cmake_path(APPEND GLOBAL_ZIG_CACHE_DIR "global")
# Used in process.version, process.versions.node, napi, and elsewhere
set(REPORTED_NODEJS_VERSION "22.3.0")
@@ -23,6 +35,7 @@ set(REPORTED_NODEJS_VERSION "22.3.0")
# If we do not set this, it will crash at startup on the first memory allocation.
if(NOT WIN32 AND NOT APPLE)
set(CMAKE_CXX_EXTENSIONS ON)
set(CMAKE_POSITION_INDEPENDENT_CODE FALSE)
endif()
# --- Build Type ---
@@ -39,6 +52,13 @@ else()
message(STATUS "The CMake build type is: ${CMAKE_BUILD_TYPE}")
endif()
if(WIN32 AND NOT CMAKE_CL_SHOWINCLUDES_PREFIX)
# workaround until cmake fix is shipped https://github.com/ninja-build/ninja/issues/2280
# './build/.ninja_deps' may need to be deleted, the bug is "Note: including file: ..." is saved
# as part of some file paths
set(CMAKE_CL_SHOWINCLUDES_PREFIX "Note: including file:")
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(DEBUG ON)
set(DEFAULT_ZIG_OPTIMIZE "Debug")
@@ -51,11 +71,8 @@ elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
set(DEFAULT_ZIG_OPTIMIZE "ReleaseFast")
if(WIN32)
# lld-link will strip it for you, so we can build directly to bun.exe
# Debug symbols are in a separate file: bun.pdb
set(bun "bun")
# TODO(@paperdave): Remove this
# it is enabled for the time being to make sure to catch more bugs in the experimental windows builds
set(DEFAULT_ZIG_OPTIMIZE "ReleaseSafe")
else()
if(ZIG_OPTIMIZE STREQUAL "Debug")
@@ -68,7 +85,7 @@ endif()
# --- MacOS SDK ---
if(APPLE AND DEFINED ENV{CI})
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0")
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.0")
endif()
if(APPLE AND NOT CMAKE_OSX_DEPLOYMENT_TARGET)
@@ -112,7 +129,11 @@ endif()
# we do some extra work afterwards to double-check, and we will rerun BUN_FIND_LLVM if the compiler did not match.
#
# If the user passes -DLLVM_PREFIX, most of this logic is skipped, but we still warn if invalid.
set(LLVM_VERSION 16)
if(WIN32 OR APPLE)
set(LLVM_VERSION 18)
else()
set(LLVM_VERSION 16)
endif()
macro(BUN_FIND_LLVM)
find_program(
@@ -146,11 +167,12 @@ macro(BUN_FIND_LLVM)
PATHS ENV PATH ${PLATFORM_LLVM_SEARCH_PATHS}
DOC "Path to LLVM ${LLVM_VERSION}'s llvm-strip binary"
)
find_program(
STRIP
NAMES strip
PATHS ENV PATH ${PLATFORM_LLVM_SEARCH_PATHS}
DOC "Path to LLVM ${LLVM_VERSION}'s llvm-strip binary"
DOC "Path to strip binary"
)
find_program(
DSYMUTIL
@@ -301,6 +323,7 @@ endif()
# -- Build Flags --
option(USE_STATIC_SQLITE "Statically link SQLite?" ${DEFAULT_ON_UNLESS_APPLE})
option(USE_CUSTOM_ZLIB "Use Bun's recommended version of zlib" ON)
option(USE_CUSTOM_LIBDEFLATE "Use Bun's recommended version of libdeflate" ON)
option(USE_CUSTOM_BORINGSSL "Use Bun's recommended version of BoringSSL" ON)
option(USE_CUSTOM_LIBARCHIVE "Use Bun's recommended version of libarchive" ON)
option(USE_CUSTOM_MIMALLOC "Use Bun's recommended version of Mimalloc" ON)
@@ -322,6 +345,11 @@ option(USE_STATIC_LIBATOMIC "Statically link libatomic, requires the presence of
option(USE_LTO "Enable Link-Time Optimization" ${DEFAULT_LTO})
if(APPLE AND USE_LTO)
set(USE_LTO OFF)
message(FATAL_ERROR "Link-Time Optimization is not supported on macOS because it requires -fuse-ld=lld and lld causes many segfaults on macOS (likely related to stack size)")
endif()
if(WIN32 AND USE_LTO)
set(CMAKE_LINKER_TYPE LLD)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF)
@@ -448,6 +476,8 @@ elseif(NOT BUN_CPP_ONLY AND NOT BUN_LINK_ONLY AND NOT BUN_TIDY_ONLY AND NOT BUN_
message(STATUS "Installed Zig Compiler: ${ZIG_COMPILER}")
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "build.zig")
message(STATUS "Using zig cache directory: ${ZIG_CACHE_DIR}")
endif()
# Bun
@@ -472,7 +502,7 @@ if(USE_UNIFIED_SOURCES)
endif()
# CCache
find_program(CCACHE_PROGRAM sccache)
# find_program(CCACHE_PROGRAM sccache)
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
@@ -638,16 +668,6 @@ file(GLOB BUN_CPP ${CONFIGURE_DEPENDS}
)
list(APPEND BUN_RAW_SOURCES ${BUN_CPP})
# -- Brotli --
set(BROTLI_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/deps/brotli")
file(GLOB BROTLI_FILES ${CONFIGURE_DEPENDS}
"${BROTLI_SRC}/common/*.c"
"${BROTLI_SRC}/enc/*.c"
"${BROTLI_SRC}/dec/*.c"
)
list(APPEND BUN_RAW_SOURCES ${BROTLI_FILES})
include_directories("${BUN_DEPS_DIR}/brotli/include")
# -- uSockets --
set(USOCKETS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/packages/bun-usockets/src")
file(GLOB USOCKETS_FILES ${CONFIGURE_DEPENDS}
@@ -684,6 +704,32 @@ add_custom_command(
)
list(APPEND BUN_RAW_SOURCES "${BUN_WORKDIR}/codegen/ZigGeneratedClasses.cpp")
if(NOT NO_CODEGEN)
# --- ErrorCode Generator ---
file(GLOB NODE_ERRORS_TS ${CONFIGURE_DEPENDS}
"${BUN_SRC}/bun.js/bindings/ErrorCode.ts"
)
add_custom_command(
OUTPUT "${BUN_WORKDIR}/codegen/ErrorCode+List.h" "${BUN_WORKDIR}/codegen/ErrorCode+Data.h" "${BUN_WORKDIR}/codegen/ErrorCode.zig"
COMMAND ${BUN_EXECUTABLE} run "${BUN_CODEGEN_SRC}/generate-node-errors.ts" "${BUN_WORKDIR}/codegen"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
MAIN_DEPENDENCY "${BUN_CODEGEN_SRC}/generate-node-errors.ts"
DEPENDS ${NODE_ERRORS_TS}
VERBATIM
COMMENT "Generating ErrorCode.zig"
)
# This needs something to force it to be regenerated
WEBKIT_ADD_SOURCE_DEPENDENCIES(
"${BUN_SRC}/bun.js/bindings/ErrorCode.cpp"
"${BUN_WORKDIR}/codegen/ErrorCode+List.h"
)
WEBKIT_ADD_SOURCE_DEPENDENCIES(
"${BUN_SRC}/bun.js/bindings/ErrorCode.h"
"${BUN_WORKDIR}/codegen/ErrorCode+Data.h"
)
endif()
# --- JSSink Generator ---
add_custom_command(
OUTPUT "${BUN_WORKDIR}/codegen/JSSink.cpp"
@@ -757,7 +803,7 @@ if(NOT NO_CODEGEN)
OUTPUT ${BUN_IDENTIFIER_CACHE_OUT}
MAIN_DEPENDENCY "${BUN_SRC}/js_lexer/identifier_data.zig"
DEPENDS "${BUN_SRC}/js_lexer/identifier_cache.zig"
COMMAND ${ZIG_COMPILER} run "--zig-lib-dir" "${ZIG_LIB_DIR}" "${BUN_SRC}/js_lexer/identifier_data.zig"
COMMAND ${ZIG_COMPILER} run "--zig-lib-dir" "${ZIG_LIB_DIR}" "--cache-dir" "${LOCAL_ZIG_CACHE_DIR}" "--global-cache-dir" "${GLOBAL_ZIG_CACHE_DIR}" "${BUN_SRC}/js_lexer/identifier_data.zig"
VERBATIM
COMMENT "Building Identifier Cache"
)
@@ -778,6 +824,8 @@ if(NOT NO_CODEGEN)
"${BUN_SRC}/js/thirdparty/*.ts"
"${BUN_SRC}/js/internal/*.js"
"${BUN_SRC}/js/internal/*.ts"
"${BUN_SRC}/js/internal/util/*.js"
"${BUN_SRC}/js/internal/fs/*.ts"
"${BUN_SRC}/js/node/*.js"
"${BUN_SRC}/js/node/*.ts"
"${BUN_SRC}/js/thirdparty/*.js"
@@ -861,13 +909,24 @@ file(GLOB ZIG_FILES
"${BUN_SRC}/*/*/*/*/*.zig"
)
if(NOT BUN_ZIG_OBJ_FORMAT)
# To use LLVM bitcode from Zig, more work needs to be done. Currently, an install of
# LLVM 18.1.7 does not compatible with what bitcode Zig 0.13 outputs (has LLVM 18.1.7)
# Change to "bc" to experiment, "Invalid record" means it is not valid output.
set(BUN_ZIG_OBJ_FORMAT "obj")
endif()
if(NOT BUN_ZIG_OBJ_DIR)
set(BUN_ZIG_OBJ_DIR "${BUN_WORKDIR}/CMakeFiles")
endif()
get_filename_component(BUN_ZIG_OBJ_DIR "${BUN_ZIG_OBJ_DIR}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
set(BUN_ZIG_OBJ "${BUN_ZIG_OBJ_DIR}/bun-zig.o")
if(WIN32)
set(BUN_ZIG_OBJ "${BUN_ZIG_OBJ_DIR}/bun-zig.o")
else()
set(BUN_ZIG_OBJ "${BUN_ZIG_OBJ_DIR}/bun-zig.o")
endif()
set(USES_TERMINAL_NOT_IN_CI "")
@@ -882,6 +941,7 @@ if(NOT BUN_LINK_ONLY AND NOT BUN_CPP_ONLY)
"${ZIG_COMPILER}" "build" "obj"
"--zig-lib-dir" "${ZIG_LIB_DIR}"
"--prefix" "${BUN_ZIG_OBJ_DIR}"
"--verbose"
"-Dgenerated-code=${BUN_WORKDIR}/codegen"
"-freference-trace=10"
"-Dversion=${Bun_VERSION}"
@@ -891,10 +951,14 @@ if(NOT BUN_LINK_ONLY AND NOT BUN_CPP_ONLY)
"-Dtarget=${ZIG_TARGET}"
"-Denable_logs=${ENABLE_LOGS}"
"-Dreported_nodejs_version=${REPORTED_NODEJS_VERSION}"
"-Dobj_format=${BUN_ZIG_OBJ_FORMAT}"
"--cache-dir" "${LOCAL_ZIG_CACHE_DIR}"
"--global-cache-dir" "${GLOBAL_ZIG_CACHE_DIR}"
DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/build.zig"
"${ZIG_FILES}"
"${BUN_WORKDIR}/codegen/ZigGeneratedClasses.zig"
"${BUN_WORKDIR}/codegen/ErrorCode.zig"
"${BUN_WORKDIR}/codegen/ResolvedSourceTag.zig"
"${BUN_IDENTIFIER_CACHE_OUT}"
"${BUN_SRC}/api/schema.zig"
@@ -980,8 +1044,20 @@ add_compile_definitions(
)
if(NOT ASSERT_ENABLED)
if(APPLE)
add_compile_definitions("_LIBCXX_ENABLE_ASSERTIONS=0")
add_compile_definitions("_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE")
endif()
add_compile_definitions("NDEBUG=1")
else()
if(APPLE)
add_compile_definitions("_LIBCXX_ENABLE_ASSERTIONS=1")
add_compile_definitions("_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_compile_definitions("_GLIBCXX_ASSERTIONS=1")
endif()
add_compile_definitions("ASSERT_ENABLED=1")
endif()
@@ -1051,12 +1127,25 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
-Werror=uninitialized
-Werror=conditional-uninitialized
-Werror=suspicious-memaccess
-Werror=int-conversion
-Werror=nonnull
-Werror=move
-Werror=sometimes-uninitialized
-Werror=unused
-Wno-unused-function
-Wno-nullability-completeness
-Werror
-fsanitize=null
-fsanitize-recover=all
-fsanitize=bounds
-fsanitize=return
-fsanitize=nullability-arg
-fsanitize=nullability-assign
-fsanitize=nullability-return
-fsanitize=returns-nonnull-attribute
-fsanitize=unreachable
)
target_link_libraries(${bun} PRIVATE -fsanitize=null)
else()
target_compile_options(${bun} PUBLIC /Od /Z7)
endif()
@@ -1067,7 +1156,7 @@ elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
if(NOT WIN32)
if(USE_LTO)
list(APPEND LTO_FLAG "-flto=full" "-emit-llvm")
list(APPEND LTO_FLAG "-flto=full" "-emit-llvm" "-fwhole-program-vtables" "-fforce-emit-vtables")
endif()
# Leave -Werror=unused off in release builds so we avoid errors from being used in ASSERT
@@ -1078,8 +1167,11 @@ elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
-Werror=uninitialized
-Werror=conditional-uninitialized
-Werror=suspicious-memaccess
-Werror=int-conversion
-Werror=nonnull
-Werror=move
-Werror=sometimes-uninitialized
-Wno-nullability-completeness
-Werror
)
else()
@@ -1088,13 +1180,36 @@ elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
if(USE_LTO)
target_compile_options(${bun} PUBLIC -Xclang -emit-llvm-bc)
# -emit-llvm seems to not be supported or under a different name on Windows.
list(APPEND LTO_FLAG "-flto=full")
list(APPEND LTO_LINK_FLAG "-flto=full")
list(APPEND LTO_LINK_FLAG "/LTCG")
list(APPEND LTO_LINK_FLAG "/OPT:REF")
list(APPEND LTO_LINK_FLAG "/OPT:NOICF")
endif()
target_compile_options(${bun} PUBLIC /O2 ${LTO_FLAG})
target_link_options(${bun} PUBLIC ${LTO_LINK_FLAG} /DEBUG:FULL)
target_compile_options(${bun} PUBLIC
/O2
${LTO_FLAG}
/Gy
/Gw
/GF
/GA
)
target_link_options(${bun} PUBLIC
${LTO_LINK_FLAG}
/DEBUG:FULL
/delayload:ole32.dll
/delayload:WINMM.dll
/delayload:dbghelp.dll
/delayload:VCRUNTIME140_1.dll
# libuv loads these two immediately, but for some reason it seems to still be slightly faster to delayload them
/delayload:WS2_32.dll
/delayload:WSOCK32.dll
/delayload:ADVAPI32.dll
/delayload:IPHLPAPI.dll
)
endif()
endif()
@@ -1112,6 +1227,11 @@ else()
# On arm macOS, we can set it to a minimum of the M1 cpu set. this might be the default already.
target_compile_options(${bun} PUBLIC "-mcpu=apple-m1")
endif()
if(NOT WIN32 AND NOT APPLE AND ARCH STREQUAL "aarch64")
# on arm64 linux, we set a minimum of armv8
target_compile_options(${bun} PUBLIC -march=armv8-a+crc -mtune=ampere1)
endif()
endif()
target_compile_options(${bun} PUBLIC -ferror-limit=${ERROR_LIMIT})
@@ -1125,24 +1245,29 @@ if(WIN32)
"BORINGSSL_NO_CXX=1" # lol
)
# set_property(TARGET ${bun} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
set_property(TARGET ${bun} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
# set_property(TARGET ${bun} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set_property(TARGET ${bun} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
target_compile_options(${bun} PUBLIC "/EHsc" "/GR-")
target_compile_options(${bun} PUBLIC "/EHsc" "/GR-" -Xclang -fno-c++-static-destructors)
target_link_options(${bun} PUBLIC "/STACK:0x1200000,0x100000" "/DEF:${BUN_SRC}/symbols.def" "/errorlimit:0")
else()
target_compile_options(${bun} PUBLIC
-fPIC
-mtune=${CPU_TARGET}
-fconstexpr-steps=2542484
-fconstexpr-depth=54
-fno-exceptions
-fno-asynchronous-unwind-tables
-fno-unwind-tables
-fno-c++-static-destructors
-fvisibility=hidden
-fvisibility-inlines-hidden
-fno-rtti
-fno-omit-frame-pointer
-mno-omit-leaf-frame-pointer
-fno-pic
-fno-pie
-faddrsig
)
endif()
@@ -1158,10 +1283,12 @@ endif()
if(UNIX AND NOT APPLE)
target_link_options(${bun} PUBLIC
"-fuse-ld=lld"
"-static-libstdc++"
"-static-libgcc"
"-Wl,-z,now"
-fuse-ld=lld-${LLVM_VERSION}
-fno-pic
-static-libstdc++
-static-libgcc
"-Wl,-no-pie"
"-Wl,-icf=safe"
"-Wl,--as-needed"
"-Wl,--gc-sections"
"-Wl,-z,stack-size=12800000"
@@ -1190,6 +1317,8 @@ if(UNIX AND NOT APPLE)
"-rdynamic"
"-Wl,--dynamic-list=${BUN_SRC}/symbols.dyn"
"-Wl,--version-script=${BUN_SRC}/linker.lds"
-Wl,-z,lazy
-Wl,-z,norelro
)
target_link_libraries(${bun} PRIVATE "c")
@@ -1223,12 +1352,16 @@ endif()
# --- Stripped Binary "bun"
if(CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT WIN32 AND NOT ASSERT_ENABLED)
# add_custom_command(
# TARGET ${bun}
# POST_BUILD
# COMMAND ${DSYMUTIL} -o ${BUN_WORKDIR}/bun.dSYM ${BUN_WORKDIR}/${bun}
# COMMENT "Stripping Symbols"
# )
# if(CI AND APPLE)
if(APPLE)
add_custom_command(
TARGET ${bun}
POST_BUILD
COMMAND ${DSYMUTIL} -o ${BUN_WORKDIR}/${bun}.dSYM ${BUN_WORKDIR}/${bun}
COMMENT "Generating .dSYM"
)
endif()
add_custom_command(
TARGET ${bun}
POST_BUILD
@@ -1298,6 +1431,19 @@ else()
target_link_libraries(${bun} PRIVATE LibArchive::LibArchive)
endif()
if(USE_CUSTOM_LIBDEFLATE)
include_directories(${BUN_DEPS_DIR}/libdeflate)
if(WIN32)
target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/deflate.lib")
else()
target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libdeflate.a")
endif()
else()
find_package(LibDeflate REQUIRED)
target_link_libraries(${bun} PRIVATE LibDeflate::LibDeflate)
endif()
if(USE_CUSTOM_MIMALLOC)
include_directories(${BUN_DEPS_DIR}/mimalloc/include)
@@ -1396,6 +1542,11 @@ if(USE_STATIC_SQLITE)
"SQLITE_ENABLE_JSON1=1"
"SQLITE_ENABLE_MATH_FUNCTIONS=1"
)
if(WIN32)
target_compile_options(sqlite3 PRIVATE /MT /U_DLL)
endif()
target_link_libraries(${bun} PRIVATE sqlite3)
message(STATUS "Using static sqlite3")
target_compile_definitions(${bun} PRIVATE "LAZY_LOAD_SQLITE=0")
@@ -1404,6 +1555,24 @@ else()
target_compile_definitions(${bun} PRIVATE "LAZY_LOAD_SQLITE=1")
endif()
# -- Brotli --
set(BROTLI_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/deps/brotli")
file(GLOB BROTLI_FILES ${CONFIGURE_DEPENDS}
"${BROTLI_SRC}/common/*.c"
"${BROTLI_SRC}/enc/*.c"
"${BROTLI_SRC}/dec/*.c"
)
add_library(brotli STATIC ${BROTLI_FILES})
target_include_directories(brotli PRIVATE "${BROTLI_SRC}/include")
target_compile_definitions(brotli PRIVATE "BROTLI_STATIC")
if(WIN32)
target_compile_options(brotli PRIVATE /MT /U_DLL)
endif()
target_link_libraries(${bun} PRIVATE brotli)
include_directories("${BUN_DEPS_DIR}/brotli/include")
if(USE_CUSTOM_LSHPACK)
include_directories(${BUN_DEPS_DIR}/ls-hpack)
@@ -1421,9 +1590,11 @@ endif()
if(NOT WIN32)
target_link_libraries(${bun} PRIVATE "${WEBKIT_LIB_DIR}/libWTF.a")
target_link_libraries(${bun} PRIVATE "${WEBKIT_LIB_DIR}/libJavaScriptCore.a")
target_link_libraries(${bun} PRIVATE "${WEBKIT_LIB_DIR}/libbmalloc.a")
if(NOT APPLE OR EXISTS "${WEBKIT_LIB_DIR}/libbmalloc.a")
target_link_libraries(${bun} PRIVATE "${WEBKIT_LIB_DIR}/libbmalloc.a")
endif()
else()
target_link_options(${bun} PRIVATE "-static")
target_link_libraries(${bun} PRIVATE
"${WEBKIT_LIB_DIR}/WTF.lib"
"${WEBKIT_LIB_DIR}/JavaScriptCore.lib"
@@ -1433,10 +1604,10 @@ else()
winmm
bcrypt
ntdll
ucrt
userenv
dbghelp
wsock32 # ws2_32 required by TransmitFile aka sendfile on windows
delayimp.lib
)
endif()

View File

@@ -5,6 +5,7 @@ If you are using Windows, please refer to [this guide](/docs/project/building-wi
{% details summary="For Ubuntu users" %}
TL;DR: Ubuntu 22.04 is suggested.
Bun currently requires `glibc >=2.32` in development which means if you're on Ubuntu 20.04 (glibc == 2.31), you may likely meet `error: undefined symbol: __libc_single_threaded `. You need to take extra configurations. Also, according to this [issue](https://github.com/llvm/llvm-project/issues/97314), LLVM 16 is no longer maintained on Ubuntu 24.04 (noble). And instead, you might want `brew` to install LLVM 16 for your Ubuntu 24.04.
{% /details %}
## Install Dependencies

View File

@@ -52,11 +52,8 @@ ENV CI 1
ENV CPU_TARGET=${CPU_TARGET}
ENV BUILDARCH=${BUILDARCH}
ENV BUN_DEPS_OUT_DIR=${BUN_DEPS_OUT_DIR}
ENV USE_LTO 1
ENV CXX=clang++-${LLVM_VERSION}
ENV CC=clang-${LLVM_VERSION}
ENV AR=/usr/bin/llvm-ar-${LLVM_VERSION}
ENV LD=lld-${LLVM_VERSION}
ENV LC_CTYPE=en_US.UTF-8
ENV LC_ALL=en_US.UTF-8
@@ -93,6 +90,8 @@ RUN install_packages \
clangd-${LLVM_VERSION} \
libc++-${LLVM_VERSION}-dev \
libc++abi-${LLVM_VERSION}-dev \
llvm-${LLVM_VERSION}-runtime \
llvm-${LLVM_VERSION}-dev \
make \
cmake \
ninja-build \
@@ -119,6 +118,15 @@ RUN install_packages \
&& ln -sf /usr/bin/lldb-${LLVM_VERSION} /usr/bin/lldb \
&& ln -sf /usr/bin/clangd-${LLVM_VERSION} /usr/bin/clangd \
&& ln -sf /usr/bin/llvm-ar-${LLVM_VERSION} /usr/bin/llvm-ar \
&& ln -sf /usr/bin/ld.lld /usr/bin/ld \
&& ln -sf /usr/bin/llvm-ranlib-${LLVM_VERSION} /usr/bin/ranlib \
&& ln -sf /usr/bin/clang /usr/bin/cc \
&& ln -sf /usr/bin/clang /usr/bin/c89 \
&& ln -sf /usr/bin/clang /usr/bin/c99 \
&& ln -sf /usr/bin/clang++ /usr/bin/c++ \
&& ln -sf /usr/bin/clang++ /usr/bin/g++ \
&& ln -sf /usr/bin/llvm-ar /usr/bin/ar \
&& ln -sf /usr/bin/clang /usr/bin/gcc \
&& arch="$(dpkg --print-architecture)" \
&& case "${arch##*-}" in \
amd64) variant="x64";; \
@@ -131,6 +139,7 @@ RUN install_packages \
&& ln -s /usr/bin/bun /usr/bin/bunx \
&& rm -rf bun-linux-${variant} bun-linux-${variant}.zip \
&& mkdir -p ${BUN_DIR} ${BUN_DEPS_OUT_DIR}
# && if [ -n "${SCCACHE_BUCKET}" ]; then \
# echo "Setting up sccache" \
# && wget https://github.com/mozilla/sccache/releases/download/v0.5.4/sccache-v0.5.4-${BUILD_MACHINE_ARCH}-unknown-linux-musl.tar.gz \
@@ -167,13 +176,14 @@ ENV CCACHE_DIR=${CCACHE_DIR}
COPY Makefile ${BUN_DIR}/Makefile
COPY src/deps/c-ares ${BUN_DIR}/src/deps/c-ares
COPY scripts ${BUN_DIR}/scripts
WORKDIR $BUN_DIR
RUN --mount=type=cache,target=${CCACHE_DIR} \
cd $BUN_DIR \
&& make c-ares \
&& rm -rf ${BUN_DIR}/src/deps/c-ares ${BUN_DIR}/Makefile
&& bash ./scripts/build-cares.sh \
&& rm -rf ${BUN_DIR}/src/deps/c-ares ${BUN_DIR}/Makefile ${BUN_DIR}/scripts
FROM bun-base as lolhtml
@@ -204,13 +214,14 @@ ENV CPU_TARGET=${CPU_TARGET}
COPY Makefile ${BUN_DIR}/Makefile
COPY src/deps/mimalloc ${BUN_DIR}/src/deps/mimalloc
COPY scripts ${BUN_DIR}/scripts
ARG CCACHE_DIR=/ccache
ENV CCACHE_DIR=${CCACHE_DIR}
RUN --mount=type=cache,target=${CCACHE_DIR} \
cd ${BUN_DIR} \
&& make mimalloc \
&& bash ./scripts/build-mimalloc.sh \
&& rm -rf src/deps/mimalloc Makefile
FROM bun-base as mimalloc-debug
@@ -240,14 +251,38 @@ ARG CCACHE_DIR=/ccache
ENV CCACHE_DIR=${CCACHE_DIR}
COPY Makefile ${BUN_DIR}/Makefile
COPY CMakeLists.txt ${BUN_DIR}/CMakeLists.txt
COPY scripts ${BUN_DIR}/scripts
COPY src/deps/zlib ${BUN_DIR}/src/deps/zlib
COPY package.json bun.lockb Makefile .gitmodules ${BUN_DIR}/
WORKDIR $BUN_DIR
RUN --mount=type=cache,target=${CCACHE_DIR} \
cd $BUN_DIR \
&& make zlib \
&& rm -rf src/deps/zlib Makefile
&& bash ./scripts/build-zlib.sh && rm -rf src/deps/zlib scripts
FROM bun-base as libdeflate
ARG BUN_DIR
ARG CPU_TARGET
ENV CPU_TARGET=${CPU_TARGET}
ARG CCACHE_DIR=/ccache
ENV CCACHE_DIR=${CCACHE_DIR}
COPY Makefile ${BUN_DIR}/Makefile
COPY CMakeLists.txt ${BUN_DIR}/CMakeLists.txt
COPY scripts ${BUN_DIR}/scripts
COPY src/deps/libdeflate ${BUN_DIR}/src/deps/libdeflate
COPY package.json bun.lockb Makefile .gitmodules ${BUN_DIR}/
WORKDIR $BUN_DIR
RUN --mount=type=cache,target=${CCACHE_DIR} \
cd $BUN_DIR \
&& bash ./scripts/build-libdeflate.sh && rm -rf src/deps/libdeflate scripts
FROM bun-base as libarchive
@@ -286,6 +321,7 @@ ARG CPU_TARGET
ENV CPU_TARGET=${CPU_TARGET}
COPY Makefile ${BUN_DIR}/Makefile
COPY scripts ${BUN_DIR}/scripts
COPY src/deps/boringssl ${BUN_DIR}/src/deps/boringssl
WORKDIR $BUN_DIR
@@ -295,7 +331,7 @@ ENV CCACHE_DIR=${CCACHE_DIR}
RUN --mount=type=cache,target=${CCACHE_DIR} \
cd ${BUN_DIR} \
&& make boringssl \
&& bash ./scripts/build-boringssl.sh \
&& rm -rf src/deps/boringssl Makefile
@@ -311,12 +347,14 @@ ENV CCACHE_DIR=${CCACHE_DIR}
COPY Makefile ${BUN_DIR}/Makefile
COPY src/deps/zstd ${BUN_DIR}/src/deps/zstd
COPY scripts ${BUN_DIR}/scripts
WORKDIR $BUN_DIR
RUN --mount=type=cache,target=${CCACHE_DIR} \
cd $BUN_DIR \
&& make zstd
&& bash ./scripts/build-zstd.sh \
&& rm -rf src/deps/zstd scripts
FROM bun-base as ls-hpack
@@ -330,12 +368,14 @@ ENV CCACHE_DIR=${CCACHE_DIR}
COPY Makefile ${BUN_DIR}/Makefile
COPY src/deps/ls-hpack ${BUN_DIR}/src/deps/ls-hpack
COPY scripts ${BUN_DIR}/scripts
WORKDIR $BUN_DIR
RUN --mount=type=cache,target=${CCACHE_DIR} \
cd $BUN_DIR \
&& make lshpack
&& bash ./scripts/build-lshpack.sh \
&& rm -rf src/deps/ls-hpack scripts
FROM bun-base-with-zig as bun-identifier-cache
@@ -393,6 +433,9 @@ COPY src ${BUN_DIR}/src
COPY CMakeLists.txt ${BUN_DIR}/CMakeLists.txt
COPY src/deps/boringssl/include ${BUN_DIR}/src/deps/boringssl/include
# for uWebSockets
COPY src/deps/libdeflate ${BUN_DIR}/src/deps/libdeflate
ARG CCACHE_DIR=/ccache
ENV CCACHE_DIR=${CCACHE_DIR}
@@ -491,11 +534,13 @@ RUN mkdir -p build bun-webkit
# lol
COPY src/bun.js/bindings/sqlite/sqlite3.c ${BUN_DIR}/src/bun.js/bindings/sqlite/sqlite3.c
COPY src/deps/brotli ${BUN_DIR}/src/deps/brotli
COPY src/symbols.dyn src/linker.lds ${BUN_DIR}/src/
COPY CMakeLists.txt ${BUN_DIR}/CMakeLists.txt
COPY --from=zlib ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/
COPY --from=libdeflate ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/
COPY --from=libarchive ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/
COPY --from=boringssl ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/
COPY --from=lolhtml ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/
@@ -505,7 +550,8 @@ COPY --from=tinycc ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/
COPY --from=c-ares ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/
COPY --from=ls-hpack ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/
COPY --from=bun-compile-zig-obj /tmp/bun-zig.o ${BUN_DIR}/build/bun-zig.o
COPY --from=bun-cpp-objects ${BUN_DIR}/build/bun-cpp-objects.a ${BUN_DIR}/build/bun-cpp-objects.a
COPY --from=bun-cpp-objects ${BUN_DIR}/build/*.a ${BUN_DIR}/build/
COPY --from=bun-cpp-objects ${BUN_DIR}/build/*.o ${BUN_DIR}/build/
COPY --from=bun-cpp-objects ${BUN_DIR}/bun-webkit/lib ${BUN_DIR}/bun-webkit/lib
WORKDIR $BUN_DIR/build

2
LATEST
View File

@@ -1 +1 @@
1.1.18
1.1.21

View File

@@ -34,6 +34,8 @@ Bun statically links these libraries:
| [`c-ares`](https://github.com/c-ares/c-ares) | MIT licensed |
| [`libicu`](https://github.com/unicode-org/icu) 72 | [license here](https://github.com/unicode-org/icu/blob/main/icu4c/LICENSE) |
| [`libbase64`](https://github.com/aklomp/base64/blob/master/LICENSE) | BSD 2-Clause |
| [`libuv`](https://github.com/libuv/libuv) (on Windows) | MIT |
| [`libdeflate`](https://github.com/ebiggers/libdeflate) | MIT |
| A fork of [`uWebsockets`](https://github.com/jarred-sumner/uwebsockets) | Apache 2.0 licensed |
| Parts of [Tigerbeetle's IO code](https://github.com/tigerbeetle/tigerbeetle/blob/532c8b70b9142c17e07737ab6d3da68d7500cbca/src/io/windows.zig#L1) | Apache 2.0 licensed |

View File

@@ -26,8 +26,11 @@ ifeq ($(ARCH_NAME_RAW),arm64)
ARCH_NAME = aarch64
DOCKER_BUILDARCH = arm64
BREW_PREFIX_PATH = /opt/homebrew
DEFAULT_MIN_MACOS_VERSION = 11.0
DEFAULT_MIN_MACOS_VERSION = 13.0
MARCH_NATIVE = -mtune=$(CPU_TARGET)
ifeq ($(OS_NAME),linux)
MARCH_NATIVE = -march=armv8-a+crc -mtune=ampere1
endif
else
ARCH_NAME = x64
DOCKER_BUILDARCH = amd64
@@ -154,7 +157,12 @@ CMAKE_FLAGS_WITHOUT_RELEASE = -DCMAKE_C_COMPILER=$(CC) \
-DCMAKE_OSX_DEPLOYMENT_TARGET=$(MIN_MACOS_VERSION) \
$(CMAKE_CXX_COMPILER_LAUNCHER_FLAG) \
-DCMAKE_AR=$(AR) \
-DCMAKE_RANLIB=$(which llvm-16-ranlib 2>/dev/null || which llvm-ranlib 2>/dev/null)
-DCMAKE_RANLIB=$(which llvm-16-ranlib 2>/dev/null || which llvm-ranlib 2>/dev/null) \
-DCMAKE_CXX_STANDARD=20 \
-DCMAKE_C_STANDARD=17 \
-DCMAKE_CXX_STANDARD_REQUIRED=ON \
-DCMAKE_C_STANDARD_REQUIRED=ON \
-DCMAKE_CXX_EXTENSIONS=ON
@@ -181,8 +189,8 @@ endif
OPTIMIZATION_LEVEL=-O3 $(MARCH_NATIVE)
DEBUG_OPTIMIZATION_LEVEL= -O1 $(MARCH_NATIVE) -gdwarf-4
CFLAGS_WITHOUT_MARCH = $(MACOS_MIN_FLAG) $(OPTIMIZATION_LEVEL) -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden
BUN_CFLAGS = $(MACOS_MIN_FLAG) $(MARCH_NATIVE) $(OPTIMIZATION_LEVEL) -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden
CFLAGS_WITHOUT_MARCH = $(MACOS_MIN_FLAG) $(OPTIMIZATION_LEVEL) -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-pie -fno-pic
BUN_CFLAGS = $(MACOS_MIN_FLAG) $(MARCH_NATIVE) $(OPTIMIZATION_LEVEL) -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-pie -fno-pic
BUN_TMP_DIR := /tmp/make-bun
CFLAGS=$(CFLAGS_WITHOUT_MARCH) $(MARCH_NATIVE)
@@ -1301,6 +1309,7 @@ jsc-build-mac-compile-debug:
-DCMAKE_BUILD_TYPE=Debug \
-DUSE_THIN_ARCHIVES=OFF \
-DENABLE_FTL_JIT=ON \
-DENABLE_MALLOC_HEAP_BREAKDOWN=ON \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DUSE_BUN_JSC_ADDITIONS=ON \
-DENABLE_BUN_SKIP_FAILING_ASSERTIONS=ON \

View File

@@ -1,20 +1,43 @@
import { run, bench } from "mitata";
import { run, bench, group } from "mitata";
import { gzipSync, gunzipSync } from "bun";
const data = new TextEncoder().encode("Hello World!".repeat(9999));
const data = await Bun.file(require.resolve("@babel/standalone/babel.min.js")).arrayBuffer();
const compressed = gzipSync(data);
bench(`roundtrip - "Hello World!".repeat(9999))`, () => {
gunzipSync(gzipSync(data));
const libraries = ["zlib"];
if (Bun.semver.satisfies(Bun.version.replaceAll("-debug", ""), ">=1.1.21")) {
libraries.push("libdeflate");
}
const options = { library: undefined };
const benchFn = (name, fn) => {
if (libraries.length > 1) {
group(name, () => {
for (const library of libraries) {
bench(library, () => {
options.library = library;
fn();
});
}
});
} else {
options.library = libraries[0];
bench(name, () => {
fn();
});
}
};
benchFn(`roundtrip - @babel/standalone/babel.min.js`, () => {
gunzipSync(gzipSync(data, options), options);
});
bench(`gzipSync("Hello World!".repeat(9999)))`, () => {
gzipSync(data);
benchFn(`gzipSync(@babel/standalone/babel.min.js`, () => {
gzipSync(data, options);
});
bench(`gunzipSync("Hello World!".repeat(9999)))`, () => {
gunzipSync(compressed);
benchFn(`gunzipSync(@babel/standalone/babel.min.js`, () => {
gunzipSync(compressed, options);
});
await run();

BIN
bench/gzip/bun.lockb Executable file

Binary file not shown.

View File

@@ -1,19 +1,22 @@
import { run, bench } from "mitata";
import { gzipSync, gunzipSync } from "zlib";
import { createRequire } from "module";
import { readFileSync } from "fs";
const data = new TextEncoder().encode("Hello World!".repeat(9999));
const require = createRequire(import.meta.url);
const data = readFileSync(require.resolve("@babel/standalone/babel.min.js"));
const compressed = gzipSync(data);
bench(`roundtrip - "Hello World!".repeat(9999))`, () => {
bench(`roundtrip - @babel/standalone/babel.min.js)`, () => {
gunzipSync(gzipSync(data));
});
bench(`gzipSync("Hello World!".repeat(9999)))`, () => {
bench(`gzipSync(@babel/standalone/babel.min.js))`, () => {
gzipSync(data);
});
bench(`gunzipSync("Hello World!".repeat(9999)))`, () => {
bench(`gunzipSync(@babel/standalone/babel.min.js))`, () => {
gunzipSync(compressed);
});

View File

@@ -7,5 +7,8 @@
"bench:node": "$NODE node.mjs",
"bench:deno": "$DENO run -A --unstable deno.js",
"bench": "bun run bench:bun && bun run bench:node && bun run bench:deno"
},
"dependencies": {
"@babel/standalone": "7.24.10"
}
}

View File

@@ -0,0 +1,31 @@
import { run, bench } from "mitata";
import { createRequire } from "module";
const require = createRequire(import.meta.url);
const db = require("better-sqlite3")("./src/northwind.sqlite");
{
const sql = db.prepare(`SELECT * FROM "Order"`);
bench('SELECT * FROM "Order"', () => {
sql.all();
});
}
{
const sql = db.prepare(`SELECT * FROM "Product"`);
bench('SELECT * FROM "Product"', () => {
sql.all();
});
}
{
const sql = db.prepare(`SELECT * FROM "OrderDetail"`);
bench('SELECT * FROM "OrderDetail"', () => {
sql.all();
});
}
await run();

View File

@@ -1,8 +1,9 @@
// Run `node --experimental-sqlite bench/sqlite/node.mjs` to run the script.
// You will need `--experimental-sqlite` flag to run this script and node v22.5.0 or higher.
import { run, bench } from "mitata";
import { createRequire } from "module";
import { DatabaseSync as Database } from "node:sqlite";
const require = createRequire(import.meta.url);
const db = require("better-sqlite3")("./src/northwind.sqlite");
const db = new Database("./src/northwind.sqlite");
{
const sql = db.prepare(`SELECT * FROM "Order"`);

211
build.zig
View File

@@ -33,8 +33,6 @@ comptime {
}
}
const default_reported_nodejs_version = "22.3.0";
const zero_sha = "0000000000000000000000000000000000000000";
const BunBuildOptions = struct {
@@ -48,9 +46,10 @@ const BunBuildOptions = struct {
sha: []const u8,
enable_logs: bool = false,
tracy_callstack_depth: u16,
reported_nodejs_version: []const u8 = default_reported_nodejs_version,
reported_nodejs_version: Version,
generated_code_dir: []const u8,
no_llvm: bool,
cached_options_module: ?*Module = null,
windows_shim: ?WindowsShim = null,
@@ -73,14 +72,7 @@ const BunBuildOptions = struct {
opts.addOption([:0]const u8, "sha", b.allocator.dupeZ(u8, this.sha) catch @panic("OOM"));
opts.addOption(bool, "baseline", this.isBaseline());
opts.addOption(bool, "enable_logs", this.enable_logs);
opts.addOption([:0]const u8, "reported_nodejs_version", b.allocator.dupeZ(u8, this.reported_nodejs_version) catch @panic("OOM"));
if (this.reported_nodejs_version.len > 0 and this.reported_nodejs_version[0] == 'v') {
@panic("Node.js version should not start with 'v'");
}
if (this.reported_nodejs_version.len == 0) {
@panic("Node.js version should not be empty");
}
opts.addOption([]const u8, "reported_nodejs_version", b.fmt("{}", .{this.reported_nodejs_version}));
const mod = opts.createModule();
this.cached_options_module = mod;
@@ -122,6 +114,23 @@ pub fn getOSGlibCVersion(os: OperatingSystem) ?Version {
};
}
pub fn getCpuModel(os: OperatingSystem, arch: Arch) ?Target.Query.CpuModel {
// https://github.com/oven-sh/bun/issues/12076
if (os == .linux and arch == .aarch64) {
return .{ .explicit = &Target.aarch64.cpu.cortex_a35 };
}
// Be explicit and ensure we do not accidentally target a newer M-series chip
if (os == .mac and arch == .aarch64) {
return .{ .explicit = &Target.aarch64.cpu.apple_m1 };
}
// note: x86_64 is dealt with in the CMake config and passed in.
// the reason for the explicit handling on aarch64 is due to troubles
// passing the exact target in via flags.
return null;
}
pub fn build(b: *Build) !void {
std.log.info("zig compiler v{s}", .{builtin.zig_version_string});
@@ -147,6 +156,14 @@ pub fn build(b: *Build) !void {
break :brk .{ os, arch };
};
// target must be refined to support older but very popular devices on
// aarch64, this means moving the minimum supported CPU to support certain
// raspberry PIs. there are also a number of cloud hosts that use virtual
// machines with surprisingly out of date versions of glibc.
if (getCpuModel(os, arch)) |cpu_model| {
target_query.cpu_model = cpu_model;
}
target_query.os_version_min = getOSVersionMin(os);
target_query.glibc_version = getOSGlibCVersion(os);
@@ -163,6 +180,10 @@ pub fn build(b: *Build) !void {
break :ref_trace if (trace == 0) null else trace;
};
const obj_format = b.option(ObjectFormat, "obj_format", "Output file for object files") orelse .obj;
const no_llvm = b.option(bool, "no_llvm", "Experiment with Zig self hosted backends. No stability guaranteed") orelse false;
var build_options = BunBuildOptions{
.target = target,
.optimize = optimize,
@@ -171,6 +192,7 @@ pub fn build(b: *Build) !void {
.arch = arch,
.generated_code_dir = generated_code_dir,
.no_llvm = no_llvm,
.version = try Version.parse(bun_version),
.canary_revision = canary: {
@@ -178,7 +200,10 @@ pub fn build(b: *Build) !void {
break :canary if (rev == 0) null else rev;
},
.reported_nodejs_version = b.option([]const u8, "reported_nodejs_version", "Reported Node.js version") orelse default_reported_nodejs_version,
.reported_nodejs_version = try Version.parse(
b.option([]const u8, "reported_nodejs_version", "Reported Node.js version") orelse
"0.0.0-unset",
),
.sha = sha: {
const sha = b.option([]const u8, "sha", "Force the git sha") orelse
@@ -224,7 +249,7 @@ pub fn build(b: *Build) !void {
var step = b.step("obj", "Build Bun's Zig code as a .o file");
var bun_obj = addBunObject(b, &build_options);
step.dependOn(&bun_obj.step);
step.dependOn(&b.addInstallFile(bun_obj.getEmittedBin(), "bun-zig.o").step);
step.dependOn(addInstallObjectFile(b, bun_obj, "bun-zig", obj_format));
}
// zig build windows-shim
@@ -252,95 +277,60 @@ pub fn build(b: *Build) !void {
// zig build check-all
{
var step = b.step("check-all", "Check for semantic analysis errors on all supported platforms");
inline for (.{
const step = b.step("check-all", "Check for semantic analysis errors on all supported platforms");
addMultiCheck(b, step, build_options, &.{
.{ .os = .windows, .arch = .x86_64 },
.{ .os = .mac, .arch = .x86_64 },
.{ .os = .mac, .arch = .aarch64 },
.{ .os = .linux, .arch = .x86_64 },
.{ .os = .linux, .arch = .aarch64 },
}) |check| {
inline for (.{ .Debug, .ReleaseFast }) |mode| {
const check_target = b.resolveTargetQuery(.{
.os_tag = OperatingSystem.stdOSTag(check.os),
.cpu_arch = check.arch,
.os_version_min = getOSVersionMin(check.os),
.glibc_version = getOSGlibCVersion(check.os),
});
var options = BunBuildOptions{
.target = check_target,
.os = check.os,
.arch = check_target.result.cpu.arch,
.optimize = mode,
.canary_revision = build_options.canary_revision,
.sha = build_options.sha,
.tracy_callstack_depth = build_options.tracy_callstack_depth,
.version = build_options.version,
.reported_nodejs_version = build_options.reported_nodejs_version,
.generated_code_dir = build_options.generated_code_dir,
};
var obj = addBunObject(b, &options);
obj.generated_bin = null;
step.dependOn(&obj.step);
}
}
});
}
// zig build check-windows
{
var step = b.step("check-windows", "Check for semantic analysis errors on Windows x64");
inline for (.{
const step = b.step("check-windows", "Check for semantic analysis errors on Windows");
addMultiCheck(b, step, build_options, &.{
.{ .os = .windows, .arch = .x86_64 },
}) |check| {
inline for (.{ .Debug, .ReleaseFast }) |mode| {
const check_target = b.resolveTargetQuery(.{
.os_tag = OperatingSystem.stdOSTag(check.os),
.cpu_arch = check.arch,
.os_version_min = getOSVersionMin(check.os),
.glibc_version = getOSGlibCVersion(check.os),
});
var options = BunBuildOptions{
.target = check_target,
.os = check.os,
.arch = check_target.result.cpu.arch,
.optimize = mode,
.canary_revision = build_options.canary_revision,
.sha = build_options.sha,
.tracy_callstack_depth = build_options.tracy_callstack_depth,
.version = build_options.version,
.reported_nodejs_version = build_options.reported_nodejs_version,
.generated_code_dir = build_options.generated_code_dir,
};
var obj = addBunObject(b, &options);
obj.generated_bin = null;
step.dependOn(&obj.step);
}
}
});
}
}
// Running `zig build` with no arguments is almost always a mistake.
// TODO: revive this error. cannot right now since ZLS runs zig build without arguments
{
// const mistake_message = b.addSystemCommand(&.{
// "echo",
// \\
// \\To build Bun from source, please use `bun run setup` instead of `zig build`"
// \\For more info, see https://bun.sh/docs/project/contributing
// \\
// \\If you want to build the zig code in isolation, run:
// \\ 'zig build obj -Dgenerated-code=./build/codegen [...opts]'
// \\
// \\If you want to test a compile without emitting an object:
// \\ 'zig build check'
// \\ 'zig build check-all' (run linux+mac+windows)
// \\
// });
pub inline fn addMultiCheck(
b: *Build,
parent_step: *Step,
root_build_options: BunBuildOptions,
to_check: []const struct { os: OperatingSystem, arch: Arch },
) void {
inline for (to_check) |check| {
inline for (.{ .Debug, .ReleaseFast }) |mode| {
const check_target = b.resolveTargetQuery(.{
.os_tag = OperatingSystem.stdOSTag(check.os),
.cpu_arch = check.arch,
.cpu_model = getCpuModel(check.os, check.arch) orelse .determined_by_cpu_arch,
.os_version_min = getOSVersionMin(check.os),
.glibc_version = getOSGlibCVersion(check.os),
});
// b.default_step.dependOn(&mistake_message.step);
var options: BunBuildOptions = .{
.target = check_target,
.os = check.os,
.arch = check_target.result.cpu.arch,
.optimize = mode,
.canary_revision = root_build_options.canary_revision,
.sha = root_build_options.sha,
.tracy_callstack_depth = root_build_options.tracy_callstack_depth,
.version = root_build_options.version,
.reported_nodejs_version = root_build_options.reported_nodejs_version,
.generated_code_dir = root_build_options.generated_code_dir,
.no_llvm = root_build_options.no_llvm,
};
var obj = addBunObject(b, &options);
obj.generated_bin = null;
parent_step.dependOn(&obj.step);
}
}
}
@@ -353,10 +343,15 @@ pub fn addBunObject(b: *Build, opts: *BunBuildOptions) *Compile {
},
.target = opts.target,
.optimize = opts.optimize,
.use_llvm = !opts.no_llvm,
.use_lld = if (opts.os == .mac) false else !opts.no_llvm,
// https://github.com/ziglang/zig/issues/17430
.pic = true,
.omit_frame_pointer = false,
.strip = false, // stripped at the end
});
obj.bundle_compiler_rt = false;
obj.formatted_panics = true;
obj.root_module.omit_frame_pointer = false;
@@ -374,9 +369,10 @@ pub fn addBunObject(b: *Build, opts: *BunBuildOptions) *Compile {
}
if (opts.os == .linux) {
obj.link_emit_relocs = true;
obj.link_eh_frame_hdr = true;
obj.link_emit_relocs = false;
obj.link_eh_frame_hdr = false;
obj.link_function_sections = true;
obj.link_data_sections = true;
if (opts.optimize == .Debug) {
obj.root_module.valgrind = true;
@@ -387,6 +383,25 @@ pub fn addBunObject(b: *Build, opts: *BunBuildOptions) *Compile {
return obj;
}
const ObjectFormat = enum {
bc,
obj,
};
pub fn addInstallObjectFile(
b: *Build,
compile: *Compile,
name: []const u8,
out_mode: ObjectFormat,
) *Step {
// bin always needed to be computed or else the compilation will do nothing. zig build system bug?
const bin = compile.getEmittedBin();
return &b.addInstallFile(switch (out_mode) {
.obj => bin,
.bc => compile.getEmittedLlvmBc(),
}, b.fmt("{s}.o", .{name})).step;
}
fn exists(path: []const u8) bool {
const file = std.fs.openFileAbsolute(path, .{ .mode = .read_only }) catch return false;
file.close();
@@ -438,6 +453,12 @@ fn addInternalPackages(b: *Build, obj: *Compile, opts: *BunBuildOptions) void {
.root_source_file = .{ .cwd_relative = resolved_source_tag_path },
});
const error_code_path = b.pathJoin(&.{ opts.generated_code_dir, "ErrorCode.zig" });
validateGeneratedPath(error_code_path);
obj.root_module.addAnonymousImport("ErrorCode", .{
.root_source_file = .{ .cwd_relative = error_code_path },
});
if (os == .windows) {
obj.root_module.addAnonymousImport("bun_shim_impl.exe", .{
.root_source_file = opts.windowsShim(b).exe.getEmittedBin(),
@@ -447,7 +468,11 @@ fn addInternalPackages(b: *Build, obj: *Compile, opts: *BunBuildOptions) void {
fn validateGeneratedPath(path: []const u8) void {
if (!exists(path)) {
std.debug.panic("{s} does not exist in generated code directory!", .{std.fs.path.basename(path)});
std.debug.panic(
\\Generated file '{s}' is missing!
\\
\\Make sure to use CMake and Ninja, or pass a manual codegen folder with '-Dgenerated-code=...'
, .{path});
}
}

BIN
bun.lockb

Binary file not shown.

View File

@@ -425,6 +425,7 @@ _bun_run_completion() {
'--external[Exclude module from transpilation (can use * wildcards). ex: -e react]:external' \
'-e[Exclude module from transpilation (can use * wildcards). ex: -e react]:external' \
'--loader[Parse files with .ext:loader, e.g. --loader .js:jsx. Valid loaders: js, jsx, ts, tsx, json, toml, text, file, wasm, napi]:loader' \
'--packages[Exclude dependencies from bundle, e.g. --packages external. Valid options: bundle, external]:packages' \
'-l[Parse files with .ext:loader, e.g. --loader .js:jsx. Valid loaders: js, jsx, ts, tsx, json, toml, text, file, wasm, napi]:loader' \
'--origin[Rewrite import URLs to start with --origin. Default: ""]:origin' \
'-u[Rewrite import URLs to start with --origin. Default: ""]:origin' \

308
docs/api/fetch.md Normal file
View File

@@ -0,0 +1,308 @@
Bun implements the WHATWG `fetch` standard, with some extensions to meet the needs of server-side JavaScript.
Bun also implements `node:http`, but `fetch` is generally recommended instead.
## Sending an HTTP request
To send an HTTP request, use `fetch`
```ts
const response = await fetch("http://example.com");
console.log(response.status); // => 200
const text = await response.text(); // or response.json(), response.formData(), etc.
```
`fetch` also works with HTTPS URLs.
```ts
const response = await fetch("https://example.com");
```
You can also pass `fetch` a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object.
```ts
const request = new Request("http://example.com", {
method: "POST",
body: "Hello, world!",
});
const response = await fetch(request);
```
### Sending a POST request
To send a POST request, pass an object with the `method` property set to `"POST"`.
```ts
const response = await fetch("http://example.com", {
method: "POST",
body: "Hello, world!",
});
```
`body` can be a string, a `FormData` object, an `ArrayBuffer`, a `Blob`, and more. See the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Body/body) for more information.
### Proxying requests
To proxy a request, pass an object with the `proxy` property set to a URL.
```ts
const response = await fetch("http://example.com", {
proxy: "http://proxy.com",
});
```
### Custom headers
To set custom headers, pass an object with the `headers` property set to an object.
```ts
const response = await fetch("http://example.com", {
headers: {
"X-Custom-Header": "value",
},
});
```
You can also set headers using the [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object.
```ts
const headers = new Headers();
headers.append("X-Custom-Header", "value");
const response = await fetch("http://example.com", {
headers,
});
```
### Response bodies
To read the response body, use one of the following methods:
- `response.text(): Promise<string>`: Returns a promise that resolves with the response body as a string.
- `response.json(): Promise<any>`: Returns a promise that resolves with the response body as a JSON object.
- `response.formData(): Promise<FormData>`: Returns a promise that resolves with the response body as a `FormData` object.
- `response.bytes(): Promise<Uint8Array>`: Returns a promise that resolves with the response body as a `Uint8Array`.
- `response.arrayBuffer(): Promise<ArrayBuffer>`: Returns a promise that resolves with the response body as an `ArrayBuffer`.
- `response.blob(): Promise<Blob>`: Returns a promise that resolves with the response body as a `Blob`.
#### Streaming response bodies
You can use async iterators to stream the response body.
```ts
const response = await fetch("http://example.com");
for await (const chunk of response.body) {
console.log(chunk);
}
```
You can also more directly access the `ReadableStream` object.
```ts
const response = await fetch("http://example.com");
const stream = response.body;
const reader = stream.getReader();
const { value, done } = await reader.read();
```
### Fetching a URL with a timeout
To fetch a URL with a timeout, use `AbortSignal.timeout`:
```ts
const response = await fetch("http://example.com", {
signal: AbortSignal.timeout(1000),
});
```
#### Canceling a request
To cancel a request, use an `AbortController`:
```ts
const controller = new AbortController();
const response = await fetch("http://example.com", {
signal: controller.signal,
});
controller.abort();
```
### Unix domain sockets
To fetch a URL using a Unix domain socket, use the `unix: string` option:
```ts
const response = await fetch("https://hostname/a/path", {
unix: "/var/run/path/to/unix.sock",
method: "POST",
body: JSON.stringify({ message: "Hello from Bun!" }),
headers: {
"Content-Type": "application/json",
},
});
```
### TLS
To use a client certificate, use the `tls` option:
```ts
await fetch("https://example.com", {
tls: {
key: Bun.file("/path/to/key.pem"),
cert: Bun.file("/path/to/cert.pem"),
// ca: [Bun.file("/path/to/ca.pem")],
},
});
```
#### Custom TLS Validation
To customize the TLS validation, use the `checkServerIdentity` option in `tls`
```ts
await fetch("https://example.com", {
tls: {
checkServerIdentity: (hostname, peerCertificate) => {
// Return an error if the certificate is invalid
},
},
});
```
This is similar to how it works in Node's `net` module.
## Debugging
To help with debugging, you can pass `verbose: true` to `fetch`:
```ts
const response = await fetch("http://example.com", {
verbose: true,
});
```
This will print the request and response headers to your terminal:
```sh
[fetch] > HTTP/1.1 GET http://example.com/
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.1.21
[fetch] > Accept: */*
[fetch] > Host: example.com
[fetch] > Accept-Encoding: gzip, deflate, br
[fetch] < 200 OK
[fetch] < Content-Encoding: gzip
[fetch] < Age: 201555
[fetch] < Cache-Control: max-age=604800
[fetch] < Content-Type: text/html; charset=UTF-8
[fetch] < Date: Sun, 21 Jul 2024 02:41:14 GMT
[fetch] < Etag: "3147526947+gzip"
[fetch] < Expires: Sun, 28 Jul 2024 02:41:14 GMT
[fetch] < Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
[fetch] < Server: ECAcc (sac/254F)
[fetch] < Vary: Accept-Encoding
[fetch] < X-Cache: HIT
[fetch] < Content-Length: 648
```
Note: `verbose: boolean` is not part of the Web standard `fetch` API and is specific to Bun.
## Performance
Before an HTTP request can be sent, the DNS lookup must be performed. This can take a significant amount of time, especially if the DNS server is slow or the network connection is poor.
After the DNS lookup, the TCP socket must be connected and the TLS handshake might need to be performed. This can also take a significant amount of time.
After the request completes, consuming the response body can also take a significant amount of time and memory.
At every step of the way, Bun provides APIs to help you optimize the performance of your application.
### DNS prefetching
To prefetch a DNS entry, you can use the `dns.prefetch` API. This API is useful when you know you'll need to connect to a host soon and want to avoid the initial DNS lookup.
```ts
import { dns } from "bun";
dns.prefetch("bun.sh", 443);
```
#### DNS caching
By default, Bun caches and deduplicates DNS queries in-memory for up to 30 seconds. You can see the cache stats by calling `dns.getCacheStats()`:
To learn more about DNS caching in Bun, see the [DNS caching](/docs/api/dns) documentation.
### Preconnect to a host
To preconnect to a host, you can use the `fetch.preconnect` API. This API is useful when you know you'll need to connect to a host soon and want to start the initial DNS lookup, TCP socket connection, and TLS handshake early.
```ts
import { fetch } from "bun";
fetch.preconnect("https://bun.sh");
```
Note: calling `fetch` immediately after `fetch.preconnect` will not make your request faster. Preconnecting only helps if you know you'll need to connect to a host soon, but you're not ready to make the request yet.
#### Preconnect at startup
To preconnect to a host at startup, you can pass `--fetch-preconnect`:
```sh
$ bun --fetch-preconnect https://bun.sh ./my-script.ts
```
This is sort of like `<link rel="preconnect">` in HTML.
This feature is not implemented on Windows yet. If you're interested in using this feature on Windows, please file an issue and we can implement support for it on Windows.
### Connection pooling & HTTP keep-alive
Bun automatically reuses connections to the same host. This is known as connection pooling. This can significantly reduce the time it takes to establish a connection. You don't need to do anything to enable this; it's automatic.
#### Simultaneous connection limit
By default, Bun limits the maximum number of simultaneous `fetch` requests to 256. We do this for several reasons:
- It improves overall system stability. Operating systems have an upper limit on the number of simultaneous open TCP sockets, usually in the low thousands. Nearing this limit causes your entire computer to behave strangely. Applications hang and crash.
- It encourages HTTP Keep-Alive connection reuse. For short-lived HTTP requests, the slowest step is often the initial connection setup. Reusing connections can save a lot of time.
When the limit is exceeded, the requests are queued and sent as soon as the next request ends.
You can increase the maximum number of simultaneous connections via the `BUN_CONFIG_MAX_HTTP_REQUESTS` environment variable:
```sh
$ BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun ./my-script.ts
```
The max value for this limit is currently set to 65,336. The maximum port number is 65,535, so it's quite difficult for any one computer to exceed this limit.
### Response buffering
Bun goes to great lengths to optimize the performance of reading the response body. The fastest way to read the response body is to use one of these methods:
- `response.text(): Promise<string>`
- `response.json(): Promise<any>`
- `response.formData(): Promise<FormData>`
- `response.bytes(): Promise<Uint8Array>`
- `response.arrayBuffer(): Promise<ArrayBuffer>`
- `response.blob(): Promise<Blob>`
You can also use `Bun.write` to write the response body to a file on disk:
```ts
import { write } from "bun";
await write("output.txt", response);
```

View File

@@ -756,6 +756,25 @@ $ bun build ./index.tsx --outdir ./out --external '*'
{% /codetabs %}
### `packages`
Control whatever package dependencies are included to bundle or not. Possible values: `bundle` (default), `external`. Bun threats any import which path do not start with `.`, `..` or `/` as package.
{% codetabs group="a" %}
```ts#JavaScript
await Bun.build({
entrypoints: ['./index.ts'],
packages: 'external',
})
```
```bash#CLI
$ bun build ./index.ts --packages external
```
{% /codetabs %}
### `naming`
Customizes the generated file names. Defaults to `./[dir]/[name].[ext]`.

View File

@@ -94,8 +94,8 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot
---
- `--packages`
- n/a
- Not supported
- `--packages`
- No differences
---
@@ -208,8 +208,7 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot
---
- `--ignore-annotations`
- n/a
- Not supported
- `--ignore-dce-annotations`
---

View File

@@ -5,8 +5,8 @@ name: Define and replace static globals & constants
The `--define` flag lets you declare statically-analyzable constants and globals. It replace all usages of an identifier or property in a JavaScript or TypeScript file with a constant value. This feature is supported at runtime and also in `bun build`. This is sort of similar to `#define` in C/C++, except for JavaScript.
```ts
bun --define:process.env.NODE_ENV="'production'" src/index.ts # Runtime
bun build --define:process.env.NODE_ENV="'production'" src/index.ts # Build
bun --define process.env.NODE_ENV="'production'" src/index.ts # Runtime
bun build --define process.env.NODE_ENV="'production'" src/index.ts # Build
```
---
@@ -95,7 +95,7 @@ To replace all usages of `AWS` with the JSON object `{"ACCESS_KEY":"abc","SECRET
```sh
# JSON
bun --define:AWS='{"ACCESS_KEY":"abc","SECRET_KEY":"def"}' src/index.ts
bun --define AWS='{"ACCESS_KEY":"abc","SECRET_KEY":"def"}' src/index.ts
```
Those will be transformed into the equivalent JavaScript code.
@@ -119,7 +119,7 @@ You can also pass properties to the `--define` flag.
For example, to replace all usages of `console.write` with `console.log`, you can use the following command (requires Bun v1.1.5 or later)
```sh
bun --define:console.write=console.log src/index.ts
bun --define console.write=console.log src/index.ts
```
That transforms the following input:

View File

@@ -27,6 +27,28 @@ data.version; // => "1.0.0"
data.author.name; // => "John Dough"
```
Bun also supports [Import Attributes](https://github.com/tc39/proposal-import-attributes/) and [JSON modules](https://github.com/tc39/proposal-json-modules) syntax.
```ts
import data from "./package.json" with { type: "json" };
data.name; // => "bun"
data.version; // => "1.0.0"
data.author.name; // => "John Dough"
```
---
Bun also supports [Import Attributes](https://github.com/tc39/proposal-import-attributes/) and [JSON modules](https://github.com/tc39/proposal-json-modules) syntax.
```ts
import data from "./package.json" with { type: "json" };
data.name; // => "bun"
data.version; // => "1.0.0"
data.author.name; // => "John Dough"
```
---
See [Docs > Runtime > TypeScript](/docs/runtime/typescript) for more information on using TypeScript with Bun.

View File

@@ -2,7 +2,7 @@ Bun supports loading configuration options from [`.npmrc`](https://docs.npmjs.co
{% callout %}
**NOTE**: We recommend migrating your `.npmrc` file to Bun's [`bunfig.toml`](/docs/runtime/bunfig) format, as it provides more flexible options and can let you configure Bun-specific configuration options.
**NOTE**: We recommend migrating your `.npmrc` file to Bun's [`bunfig.toml`](/docs/runtime/bunfig) format, as it provides more flexible options and can let you configure Bun-specific options.
{% /callout %}
@@ -50,16 +50,22 @@ Allows you to set options for a specific registry:
# or you could set a username and password
# note that the password is base64 encoded
//http://localhost:4873/:username=myusername
//http://localhost:4873/:_password=${NPM_PASSWORD}
# or use _auth, which is your username and password
# combined into a single string, which is then base 64 encoded
//http://localhost:4873/:_auth=${NPM_AUTH}
```
The following options are supported:
- `_authToken`
- `username`
- `_password`
- `_password` (base64 encoded password)
- `_auth` (base64 encoded username:password, e.g. `btoa(username + ":" + password)`)
The equivalent `bunfig.toml` option is to add a key in [`install.scopes`](/docs/runtime/bunfig#install-registry):

View File

@@ -287,8 +287,11 @@ export default {
divider("API"),
page("api/http", "HTTP server", {
description: `Bun implements Web-standard fetch, plus a Bun-native API for building fast HTTP servers.`,
description: `Bun implements a fast HTTP server built on Request/Response objects, along with supporting node:http APIs.`,
}), // "`Bun.serve`"),
page("api/fetch", "HTTP client", {
description: `Bun implements Web-standard fetch with some Bun-native extensions.`,
}), // "fetch"),
page("api/websockets", "WebSockets", {
description: `Bun supports server-side WebSockets with on-the-fly compression, TLS support, and a Bun-native pubsub API.`,
}), // "`Bun.serve`"),

View File

@@ -171,6 +171,8 @@ Once imported, you should see something like this:
{% image alt="Viewing heap snapshot in Safari" src="https://user-images.githubusercontent.com/709451/204429337-b0d8935f-3509-4071-b991-217794d1fb27.png" caption="Viewing heap snapshot in Safari Dev Tools" /%}
> The [web debugger](https://bun.sh/docs/runtime/debugger#inspect) also offers the timeline feature which allows you to track and examine the memory usage of the running debug session.
### Native heap stats
Bun uses mimalloc for the other heap. To report a summary of non-JavaScript memory usage, set the `MIMALLOC_SHOW_STATS=1` environment variable. and stats will print on exit.

View File

@@ -60,7 +60,7 @@ Visual Studio can be installed graphically using the wizard or through WinGet:
After Visual Studio, you need the following:
- LLVM 16
- LLVM 18.1.8
- Go
- Rust
- NASM
@@ -78,14 +78,14 @@ After Visual Studio, you need the following:
```ps1#WinGet
## Select "Add LLVM to the system PATH for all users" in the LLVM installer
> winget install -i LLVM.LLVM -v 16.0.6 && winget install GoLang.Go Rustlang.Rustup NASM.NASM StrawberryPerl.StrawberryPerl RubyInstallerTeam.Ruby.3.2 OpenJS.NodeJS.LTS
> winget install -i LLVM.LLVM -v 18.1.8 && winget install GoLang.Go Rustlang.Rustup NASM.NASM StrawberryPerl.StrawberryPerl RubyInstallerTeam.Ruby.3.2 OpenJS.NodeJS.LTS
```
```ps1#Scoop
> irm https://get.scoop.sh | iex
> scoop install nodejs-lts go rust nasm ruby perl
# scoop seems to be buggy if you install llvm and the rest at the same time
> scoop install llvm@16.0.6
> scoop install llvm@18.1.8
```
{% /codetabs %}

View File

@@ -153,7 +153,7 @@ Some methods are not optimized yet.
### [`node:util`](https://nodejs.org/api/util.html)
🟡 Missing `MIMEParams` `MIMEType` `aborted` `debug` `getSystemErrorMap` `getSystemErrorName` `transferableAbortController` `transferableAbortSignal` `stripVTControlCharacters`
🟡 Missing `MIMEParams` `MIMEType` `aborted` `debug` `getSystemErrorMap` `transferableAbortController` `transferableAbortSignal` `stripVTControlCharacters`
### [`node:v8`](https://nodejs.org/api/v8.html)

View File

@@ -7,22 +7,36 @@ The following Web APIs are partially or completely supported.
---
- HTTP
- [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
- [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch)
[`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
[`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
[`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers)
[`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
[`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
---
- URLs
- [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
- [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL)
[`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
---
- Web Workers
- [`Worker`](https://developer.mozilla.org/en-US/docs/Web/API/Worker) [`self.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope/postMessage) [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) [`MessagePort`](https://developer.mozilla.org/en-US/docs/Web/API/MessagePort) [`MessageChannel`](https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel), [`BroadcastChannel`](https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel).
- [`Worker`](https://developer.mozilla.org/en-US/docs/Web/API/Worker)
[`self.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope/postMessage)
[`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone)
[`MessagePort`](https://developer.mozilla.org/en-US/docs/Web/API/MessagePort)
[`MessageChannel`](https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel), [`BroadcastChannel`](https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel).
---
- Streams
- [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream) [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream) [`ByteLengthQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/ByteLengthQueuingStrategy) [`CountQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/CountQueuingStrategy) and associated classes
- [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)
[`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream)
[`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream)
[`ByteLengthQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/ByteLengthQueuingStrategy)
[`CountQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/CountQueuingStrategy) and associated classes
---
@@ -37,7 +51,10 @@ The following Web APIs are partially or completely supported.
---
- Encoding and decoding
- [`atob`](https://developer.mozilla.org/en-US/docs/Web/API/atob) [`btoa`](https://developer.mozilla.org/en-US/docs/Web/API/btoa) [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder) [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder)
- [`atob`](https://developer.mozilla.org/en-US/docs/Web/API/atob)
[`btoa`](https://developer.mozilla.org/en-US/docs/Web/API/btoa)
[`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder)
[`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder)
---
@@ -47,7 +64,8 @@ The following Web APIs are partially or completely supported.
---
- Timeouts
- [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout) [`clearTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/clearTimeout)
- [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout)
[`clearTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/clearTimeout)
---
@@ -57,14 +75,16 @@ The following Web APIs are partially or completely supported.
---
- Crypto
- [`crypto`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto) [`SubtleCrypto`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto)
- [`crypto`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto)
[`SubtleCrypto`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto)
[`CryptoKey`](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey)
---
- Debugging
- [`console`](https://developer.mozilla.org/en-US/docs/Web/API/console) [`performance`](https://developer.mozilla.org/en-US/docs/Web/API/Performance)
- [`console`](https://developer.mozilla.org/en-US/docs/Web/API/console)
[`performance`](https://developer.mozilla.org/en-US/docs/Web/API/Performance)
---
@@ -79,7 +99,9 @@ The following Web APIs are partially or completely supported.
---
- User interaction
- [`alert`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert) [`confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm) [`prompt`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt) (intended for interactive CLIs)
- [`alert`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert)
[`confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm)
[`prompt`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt) (intended for interactive CLIs)
<!-- - Blocking. Prints the alert message to terminal and awaits `[ENTER]` before proceeding. -->
<!-- - Blocking. Prints confirmation message and awaits `[y/N]` input from user. Returns `true` if user entered `y` or `Y`, `false` otherwise.
@@ -94,7 +116,10 @@ The following Web APIs are partially or completely supported.
- Events
- [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget)
[`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) [`ErrorEvent`](https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent) [`CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) [`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent)
[`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event)
[`ErrorEvent`](https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent)
[`CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent)
[`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent)
---

View File

@@ -196,3 +196,41 @@ As of Bun v1.0.19, Bun automatically resolves the `specifier` argument to `mock.
After resolution, the mocked module is stored in the ES Module registry **and** the CommonJS require cache. This means that you can use `import` and `require` interchangeably for mocked modules.
The callback function is called lazily, only if the module is imported or required. This means that you can use `mock.module()` to mock modules that don't exist yet, and it means that you can use `mock.module()` to mock modules that are imported by other modules.
## Restore all function mocks to their original values with `mock.restore()`
Instead of manually restoring each mock individually with `mockFn.mockRestore()`, restore all mocks with one command by calling `mock.restore()`. Doing so does not reset the value of modules overridden with `mock.module()`.
Using `mock.restore()` can reduce the amount of code in your tests by adding it to `afterEach` blocks in each test file or even in your [test preload code](https://bun.sh/docs/runtime/bunfig#test-preload).
```ts
import { expect, mock, spyOn, test } from "bun:test";
import * as fooModule from './foo.ts';
import * as barModule from './bar.ts';
import * as bazModule from './baz.ts';
test('foo, bar, baz', () => {
const fooSpy = spyOn(fooModule, 'foo');
const barSpy = spyOn(barModule, 'bar');
const bazSpy = spyOn(bazModule, 'baz');
expect(fooSpy).toBe('foo');
expect(barSpy).toBe('bar');
expect(bazSpy).toBe('baz');
fooSpy.mockImplementation(() => 42);
barSpy.mockImplementation(() => 43);
bazSpy.mockImplementation(() => 44);
expect(fooSpy).toBe(42);
expect(barSpy).toBe(43);
expect(bazSpy).toBe(44);
mock.restore();
expect(fooSpy).toBe('foo');
expect(barSpy).toBe('bar');
expect(bazSpy).toBe('baz');
});
```

View File

@@ -195,7 +195,6 @@ pub fn main() anyerror!void {
args.headers_buf,
response_body_string,
args.body,
0,
HTTP.FetchRedirect.follow,
),
};

View File

@@ -31,7 +31,6 @@ const params = [_]clap.Param(clap.Help){
clap.parseParam("-b, --body <STR> HTTP request body as a string") catch unreachable,
clap.parseParam("-f, --file <STR> File path to load as body") catch unreachable,
clap.parseParam("-n, --count <INT> How many runs? Default 10") catch unreachable,
clap.parseParam("-t, --timeout <INT> Max duration per request") catch unreachable,
clap.parseParam("-r, --retry <INT> Max retry count") catch unreachable,
clap.parseParam("--no-gzip Disable gzip") catch unreachable,
clap.parseParam("--no-deflate Disable deflate") catch unreachable,
@@ -75,7 +74,6 @@ pub const Arguments = struct {
body: string = "",
turbo: bool = false,
count: usize = 10,
timeout: usize = 0,
repeat: usize = 0,
concurrency: u16 = 32,
@@ -165,10 +163,6 @@ pub const Arguments = struct {
// .keep_alive = !args.flag("--no-keep-alive"),
.concurrency = std.fmt.parseInt(u16, args.option("--max-concurrency") orelse "32", 10) catch 32,
.turbo = args.flag("--turbo"),
.timeout = std.fmt.parseInt(usize, args.option("--timeout") orelse "0", 10) catch |err| {
Output.prettyErrorln("<r><red>{s}<r> parsing timeout", .{@errorName(err)});
Global.exit(1);
},
.count = std.fmt.parseInt(usize, args.option("--count") orelse "10", 10) catch |err| {
Output.prettyErrorln("<r><red>{s}<r> parsing count", .{@errorName(err)});
Global.exit(1);
@@ -225,7 +219,6 @@ pub fn main() anyerror!void {
args.headers_buf,
response_body,
"",
args.timeout,
),
};
ctx.http.client.verbose = args.verbose;

View File

@@ -4,7 +4,7 @@
"workspaces": [
"./packages/bun-types"
],
"dependencies": {
"devDependencies": {
"@vscode/debugadapter": "^1.65.0",
"esbuild": "^0.21.4",
"eslint": "^9.4.0",
@@ -15,9 +15,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"source-map-js": "^1.2.0",
"typescript": "^5.4.5"
},
"devDependencies": {
"typescript": "^5.4.5",
"@types/bun": "^1.1.3",
"@types/react": "^18.3.3",
"@typescript-eslint/eslint-plugin": "^7.11.0",

View File

@@ -5,7 +5,7 @@
"std.debug.assert": "Use bun.assert instead",
"std.debug.dumpStackTrace": "Use bun.handleErrorReturnTrace or bun.crash_handler.dumpStackTrace instead",
"std.debug.print": "Don't let this be committed",
"std.mem.indexOfAny(": "Use bun.strings.indexOfAny",
"std.mem.indexOfAny(u8": "Use bun.strings.indexOfAny",
"undefined != ": "This is by definition Undefined Behavior.",
"undefined == ": "This is by definition Undefined Behavior.",
"bun.toFD(std.fs.cwd().fd)": "Use bun.FD.cwd()",
@@ -13,5 +13,6 @@
"std.StringArrayHashMap(": "bun.StringArrayHashMap has a faster `eql`",
"std.StringHashMapUnmanaged(": "bun.StringHashMapUnmanaged has a faster `eql`",
"std.StringHashMap(": "bun.StringHashMaphas a faster `eql`",
"std.enums.tagName(": "Use bun.tagName instead",
"": ""
}

View File

@@ -239,7 +239,7 @@ Starting "${testFileName}"
GITHUB_ACTIONS: process.env.GITHUB_ACTIONS ?? "true",
BUN_DEBUG_QUIET_LOGS: "1",
BUN_INSTALL_CACHE_DIR: join(TMPDIR, ".bun-install-cache"),
BUN_ENABLE_CRASH_REPORTING: "1",
BUN_ENABLE_CRASH_REPORTING: "0",
[windows ? "TEMP" : "TMPDIR"]: TMPDIR,
},
});

View File

@@ -22,10 +22,10 @@ bun upgrade
- [Linux, arm64](https://www.npmjs.com/package/@oven/bun-linux-aarch64)
- [Linux, x64](https://www.npmjs.com/package/@oven/bun-linux-x64)
- [Linux, x64 (without AVX2 instructions)](https://www.npmjs.com/package/@oven/bun-linux-x64-baseline)
- [Windows (using Windows Subsystem for Linux, aka. "WSL")](https://relatablecode.com/how-to-set-up-bun-on-a-windows-machine)
- [Windows](https://www.npmjs.com/package/@oven/bun-windows-x64)
- [Windows (without AVX2 instructions)](https://www.npmjs.com/package/@oven/bun-windows-x64-baseline)
### Future Platforms
- [Windows](https://github.com/oven-sh/bun/issues/43)
- Unix-like variants such as FreeBSD, OpenBSD, etc.
- Android and iOS

View File

@@ -1455,7 +1455,7 @@ declare module "bun" {
* ```js
* const {imports, exports} = transpiler.scan(`
* import {foo} from "baz";
* const hello = "hi!";
* export const hello = "hi!";
* `);
*
* console.log(imports); // ["baz"]
@@ -1516,6 +1516,7 @@ declare module "bun" {
plugins?: BunPlugin[];
// manifest?: boolean; // whether to return manifest
external?: string[];
packages?: "bundle" | "external";
publicPath?: string;
define?: Record<string, string>;
// origin?: string; // e.g. http://mydomain.com
@@ -1536,6 +1537,16 @@ declare module "bun" {
syntax?: boolean;
identifiers?: boolean;
};
/**
* Ignore dead code elimination/tree-shaking annotations such as @__PURE__ and package.json
* "sideEffects" fields. This should only be used as a temporary workaround for incorrect
* annotations in libraries.
*/
ignoreDCEAnnotations?: boolean;
/**
* Force emitting @__PURE__ annotations even if minify.whitespace is true.
*/
emitDCEAnnotations?: boolean;
// treeshaking?: boolean;
// jsx?:
@@ -3099,6 +3110,10 @@ declare module "bun" {
*/
function openInEditor(path: string, options?: EditorOptions): void;
const fetch: typeof globalThis.fetch & {
preconnect(url: string): void;
};
interface EditorOptions {
editor?: "vscode" | "subl";
line?: number;
@@ -3476,6 +3491,13 @@ declare module "bun" {
* Filtered data consists mostly of small values with a somewhat random distribution.
*/
strategy?: number;
library?: "zlib";
}
interface LibdeflateCompressionOptions {
level?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
library?: "libdeflate";
}
/**
@@ -3484,26 +3506,38 @@ declare module "bun" {
* @param options Compression options to use
* @returns The output buffer with the compressed data
*/
function deflateSync(data: Uint8Array | string | ArrayBuffer, options?: ZlibCompressionOptions): Uint8Array;
function deflateSync(
data: Uint8Array | string | ArrayBuffer,
options?: ZlibCompressionOptions | LibdeflateCompressionOptions,
): Uint8Array;
/**
* Compresses a chunk of data with `zlib` GZIP algorithm.
* @param data The buffer of data to compress
* @param options Compression options to use
* @returns The output buffer with the compressed data
*/
function gzipSync(data: Uint8Array | string | ArrayBuffer, options?: ZlibCompressionOptions): Uint8Array;
function gzipSync(
data: Uint8Array | string | ArrayBuffer,
options?: ZlibCompressionOptions | LibdeflateCompressionOptions,
): Uint8Array;
/**
* Decompresses a chunk of data with `zlib` INFLATE algorithm.
* @param data The buffer of data to decompress
* @returns The output buffer with the decompressed data
*/
function inflateSync(data: Uint8Array | string | ArrayBuffer): Uint8Array;
function inflateSync(
data: Uint8Array | string | ArrayBuffer,
options?: ZlibCompressionOptions | LibdeflateCompressionOptions,
): Uint8Array;
/**
* Decompresses a chunk of data with `zlib` GUNZIP algorithm.
* @param data The buffer of data to decompress
* @returns The output buffer with the decompressed data
*/
function gunzipSync(data: Uint8Array | string | ArrayBuffer): Uint8Array;
function gunzipSync(
data: Uint8Array | string | ArrayBuffer,
options?: ZlibCompressionOptions | LibdeflateCompressionOptions,
): Uint8Array;
type Target =
/**
@@ -3823,7 +3857,7 @@ declare module "bun" {
*/
const isMainThread: boolean;
interface Socket<Data = undefined> {
interface Socket<Data = undefined> extends Disposable {
/**
* Write `data` to the socket
*
@@ -4105,7 +4139,7 @@ declare module "bun" {
setMaxSendFragment(size: number): boolean;
}
interface SocketListener<Data = undefined> {
interface SocketListener<Data = undefined> extends Disposable {
stop(closeActiveConnections?: boolean): void;
ref(): void;
unref(): void;

View File

@@ -907,26 +907,42 @@ declare global {
new (): ShadowRealm;
};
/**
* Send a HTTP(s) request
*
* @param request Request object
* @param init A structured value that contains settings for the fetch() request.
*
* @returns A promise that resolves to {@link Response} object.
*/
interface Fetch {
/**
* Send a HTTP(s) request
*
* @param request Request object
* @param init A structured value that contains settings for the fetch() request.
*
* @returns A promise that resolves to {@link Response} object.
*/
(request: Request, init?: RequestInit): Promise<Response>;
// tslint:disable-next-line:unified-signatures
function fetch(request: Request, init?: RequestInit): Promise<Response>;
/**
* Send a HTTP(s) request
*
* @param url URL string
* @param init A structured value that contains settings for the fetch() request.
*
* @returns A promise that resolves to {@link Response} object.
*/
function fetch(url: string | URL | Request, init?: FetchRequestInit): Promise<Response>;
/**
* Send a HTTP(s) request
*
* @param url URL string
* @param init A structured value that contains settings for the fetch() request.
*
* @returns A promise that resolves to {@link Response} object.
*/
(url: string | URL | Request, init?: FetchRequestInit): Promise<Response>;
(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>;
/**
* Start the DNS resolution, TCP connection, and TLS handshake for a request
* before the request is actually sent.
*
* This can reduce the latency of a request when you know there's some
* long-running task that will delay the request starting.
*
* This is a bun-specific API and is not part of the Fetch API specification.
*/
preconnect(url: string | URL): void;
}
var fetch: Fetch;
function queueMicrotask(callback: (...args: any[]) => void): void;
/**

View File

@@ -26,6 +26,10 @@
#include <stdlib.h>
#ifndef _WIN32
// Necessary for the stdint include
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -38,8 +42,8 @@
#include <mstcpip.h>
#endif
#if defined(__APPLE__) && defined(__aarch64__)
#define HAS_MSGX
#if defined(__APPLE__)
extern int Bun__doesMacOSVersionSupportSendRecvMsgX();
#endif
@@ -73,32 +77,30 @@ int bsd_sendmmsg(LIBUS_SOCKET_DESCRIPTOR fd, struct udp_sendbuf* sendbuf, int fl
}
return sendbuf->num;
#elif defined(__APPLE__)
// TODO figure out why sendmsg_x fails when one of the messages is empty
// so that we can get rid of this code.
// One of the weird things is that once a non-empty message has been sent on the socket,
// empty messages start working as well. Bizzare.
#ifdef HAS_MSGX
if (sendbuf->has_empty) {
#endif
for (int i = 0; i < sendbuf->num; i++) {
while (1) {
ssize_t ret = sendmsg(fd, &sendbuf->msgvec[i].msg_hdr, flags);
if (ret < 0) {
if (errno == EINTR) continue;
if (errno == EAGAIN || errno == EWOULDBLOCK) return i;
return ret;
}
break;
}
// sendmsg_x does not support addresses.
if (!sendbuf->has_empty && !sendbuf->has_addresses && Bun__doesMacOSVersionSupportSendRecvMsgX()) {
while (1) {
int ret = sendmsg_x(fd, sendbuf->msgvec, sendbuf->num, flags);
if (ret >= 0) return ret;
// If we receive EMMSGSIZE, we should use the fallback code.
if (errno == EMSGSIZE) break;
if (errno != EINTR) return ret;
}
return sendbuf->num;
#ifdef HAS_MSGX
}
while (1) {
int ret = sendmsg_x(fd, sendbuf->msgvec, sendbuf->num, flags);
if (ret >= 0 || errno != EINTR) return ret;
for (size_t i = 0, count = sendbuf->num; i < count; i++) {
while (1) {
ssize_t ret = sendmsg(fd, &sendbuf->msgvec[i].msg_hdr, flags);
if (ret < 0) {
if (errno == EINTR) continue;
if (errno == EAGAIN || errno == EWOULDBLOCK) return i;
return ret;
}
break;
}
}
#endif
return sendbuf->num;
#else
while (1) {
int ret = sendmmsg(fd, sendbuf->msgvec, sendbuf->num, flags | MSG_NOSIGNAL);
@@ -120,12 +122,13 @@ int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, struct udp_recvbuf *recvbuf, int fl
return 1;
}
#elif defined(__APPLE__)
#ifdef HAS_MSGX
while (1) {
int ret = recvmsg_x(fd, recvbuf->msgvec, LIBUS_UDP_RECV_COUNT, flags);
if (ret >= 0 || errno != EINTR) return ret;
if (Bun__doesMacOSVersionSupportSendRecvMsgX()) {
while (1) {
int ret = recvmsg_x(fd, recvbuf->msgvec, LIBUS_UDP_RECV_COUNT, flags);
if (ret >= 0 || errno != EINTR) return ret;
}
}
#else
for (int i = 0; i < LIBUS_UDP_RECV_COUNT; ++i) {
while (1) {
ssize_t ret = recvmsg(fd, &recvbuf->msgvec[i].msg_hdr, flags);
@@ -139,7 +142,6 @@ int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, struct udp_recvbuf *recvbuf, int fl
}
}
return LIBUS_UDP_RECV_COUNT;
#endif
#else
while (1) {
int ret = recvmmsg(fd, (struct mmsghdr *)&recvbuf->msgvec, LIBUS_UDP_RECV_COUNT, flags, 0);
@@ -154,19 +156,20 @@ void bsd_udp_setup_recvbuf(struct udp_recvbuf *recvbuf, void *databuf, size_t da
recvbuf->buflen = databuflen;
#else
// assert(databuflen > LIBUS_UDP_MAX_SIZE * LIBUS_UDP_RECV_COUNT);
for (int i = 0; i < LIBUS_UDP_RECV_COUNT; i++) {
memset(recvbuf, 0, sizeof(struct udp_recvbuf));
for (size_t i = 0; i < LIBUS_UDP_RECV_COUNT; i++) {
recvbuf->iov[i].iov_base = (char*)databuf + i * LIBUS_UDP_MAX_SIZE;
recvbuf->iov[i].iov_len = LIBUS_UDP_MAX_SIZE;
recvbuf->msgvec[i].msg_hdr.msg_name = &recvbuf->addr[i];
recvbuf->msgvec[i].msg_hdr.msg_namelen = sizeof(struct sockaddr_storage);
recvbuf->msgvec[i].msg_hdr.msg_iov = &recvbuf->iov[i];
recvbuf->msgvec[i].msg_hdr.msg_iovlen = 1;
recvbuf->msgvec[i].msg_hdr.msg_control = recvbuf->control[i];
recvbuf->msgvec[i].msg_hdr.msg_controllen = 256;
struct msghdr mh = {};
memset(&mh, 0, sizeof(struct msghdr));
mh.msg_name = &recvbuf->addr[i];
mh.msg_namelen = sizeof(struct sockaddr_storage);
mh.msg_iov = &recvbuf->iov[i];
mh.msg_iovlen = 1;
mh.msg_control = recvbuf->control[i];
mh.msg_controllen = sizeof(recvbuf->control[i]);
recvbuf->msgvec[i].msg_hdr = mh;
}
#endif
}
@@ -179,7 +182,12 @@ int bsd_udp_setup_sendbuf(struct udp_sendbuf *buf, size_t bufsize, void** payloa
buf->num = num;
return num;
#else
// TODO: can we skip empty messages altogether? Do we really need to send 0-length messages?
buf->has_empty = 0;
// sendmsg_x docs states it does not support addresses.
buf->has_addresses = 0;
struct mmsghdr *msgvec = buf->msgvec;
// todo check this math
size_t count = (bufsize - sizeof(struct udp_sendbuf)) / (sizeof(struct mmsghdr) + sizeof(struct iovec));
@@ -194,6 +202,9 @@ int bsd_udp_setup_sendbuf(struct udp_sendbuf *buf, size_t bufsize, void** payloa
addr_len = addr->sa_family == AF_INET ? sizeof(struct sockaddr_in)
: addr->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6)
: 0;
if (addr_len > 0) {
buf->has_addresses = 1;
}
}
iov[i].iov_base = payloads[i];
iov[i].iov_len = lengths[i];
@@ -206,6 +217,7 @@ int bsd_udp_setup_sendbuf(struct udp_sendbuf *buf, size_t bufsize, void** payloa
msgvec[i].msg_hdr.msg_flags = 0;
msgvec[i].msg_len = 0;
if (lengths[i] == 0) {
buf->has_empty = 1;
}
@@ -882,7 +894,7 @@ int bsd_connect_udp_socket(LIBUS_SOCKET_DESCRIPTOR fd, const char *host, int por
}
freeaddrinfo(result);
return LIBUS_SOCKET_ERROR;
return (int)LIBUS_SOCKET_ERROR;
}
int bsd_disconnect_udp_socket(LIBUS_SOCKET_DESCRIPTOR fd) {

View File

@@ -15,18 +15,17 @@
* limitations under the License.
*/
#include "libusockets.h"
#include "internal/internal.h"
#include "libusockets.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#ifndef _WIN32
#include <arpa/inet.h>
#endif
#define CONCURRENT_CONNECTIONS 2
// clang-format off
int default_is_low_prio_handler(struct us_socket_t *s) {
return 0;
}
@@ -44,7 +43,7 @@ int us_raw_root_certs(struct us_cert_string_t**out){
void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) {
/* us_listen_socket_t extends us_socket_t so we close in similar ways */
if (!us_socket_is_closed(0, &ls->s)) {
us_internal_socket_context_unlink_listen_socket(ls->s.context, ls);
us_internal_socket_context_unlink_listen_socket(ssl, ls->s.context, ls);
us_poll_stop((struct us_poll_t *) &ls->s, ls->s.context->loop);
bsd_close_socket(us_poll_fd((struct us_poll_t *) &ls->s));
@@ -65,6 +64,7 @@ void us_socket_context_close(int ssl, struct us_socket_context_t *context) {
while (ls) {
struct us_listen_socket_t *nextLS = (struct us_listen_socket_t *) ls->s.next;
us_listen_socket_close(ssl, ls);
ls = nextLS;
}
@@ -72,12 +72,12 @@ void us_socket_context_close(int ssl, struct us_socket_context_t *context) {
struct us_socket_t *s = context->head_sockets;
while (s) {
struct us_socket_t *nextS = s->next;
us_socket_close(ssl, s, 0, 0);
us_socket_close(ssl, s, LIBUS_SOCKET_CLOSE_CODE_CLEAN_SHUTDOWN, 0);
s = nextS;
}
}
void us_internal_socket_context_unlink_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *ls) {
void us_internal_socket_context_unlink_listen_socket(int ssl, struct us_socket_context_t *context, struct us_listen_socket_t *ls) {
/* We have to properly update the iterator used to sweep sockets for timeouts */
if (ls == (struct us_listen_socket_t *) context->iterator) {
context->iterator = ls->s.next;
@@ -95,9 +95,10 @@ void us_internal_socket_context_unlink_listen_socket(struct us_socket_context_t
ls->s.next->prev = ls->s.prev;
}
}
us_socket_context_unref(ssl, context);
}
void us_internal_socket_context_unlink_socket(struct us_socket_context_t *context, struct us_socket_t *s) {
void us_internal_socket_context_unlink_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s) {
/* We have to properly update the iterator used to sweep sockets for timeouts */
if (s == context->iterator) {
context->iterator = s->next;
@@ -115,6 +116,7 @@ void us_internal_socket_context_unlink_socket(struct us_socket_context_t *contex
s->next->prev = s->prev;
}
}
us_socket_context_unref(ssl, context);
}
/* We always add in the top, so we don't modify any s.next */
@@ -126,6 +128,7 @@ void us_internal_socket_context_link_listen_socket(struct us_socket_context_t *c
context->head_listen_sockets->s.prev = &ls->s;
}
context->head_listen_sockets = ls;
context->ref_count++;
}
/* We always add in the top, so we don't modify any s.next */
@@ -137,6 +140,7 @@ void us_internal_socket_context_link_socket(struct us_socket_context_t *context,
context->head_sockets->prev = s;
}
context->head_sockets = s;
context->ref_count++;
}
struct us_loop_t *us_socket_context_loop(int ssl, struct us_socket_context_t *context) {
@@ -231,6 +235,7 @@ struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t *
struct us_socket_context_t *context = us_calloc(1, sizeof(struct us_socket_context_t) + context_ext_size);
context->loop = loop;
context->is_low_prio = default_is_low_prio_handler;
context->ref_count = 1;
us_internal_loop_link(loop, context);
@@ -252,6 +257,7 @@ struct us_socket_context_t *us_create_bun_socket_context(int ssl, struct us_loop
struct us_socket_context_t *context = us_calloc(1, sizeof(struct us_socket_context_t) + context_ext_size);
context->loop = loop;
context->is_low_prio = default_is_low_prio_handler;
context->ref_count = 1;
us_internal_loop_link(loop, context);
@@ -272,7 +278,8 @@ struct us_bun_verify_error_t us_socket_verify_error(int ssl, struct us_socket_t
}
void us_socket_context_free(int ssl, struct us_socket_context_t *context) {
void us_internal_socket_context_free(int ssl, struct us_socket_context_t *context) {
#ifndef LIBUS_NO_SSL
if (ssl) {
/* This function will call us again with SSL=false */
@@ -285,7 +292,23 @@ void us_socket_context_free(int ssl, struct us_socket_context_t *context) {
* This is the opposite order compared to when creating the context - SSL code is cleaning up before non-SSL */
us_internal_loop_unlink(context->loop, context);
us_free(context);
/* Link this context to the close-list and let it be deleted after this iteration */
context->next = context->loop->data.closed_context_head;
context->loop->data.closed_context_head = context;
}
void us_socket_context_ref(int ssl, struct us_socket_context_t *context) {
context->ref_count++;
}
void us_socket_context_unref(int ssl, struct us_socket_context_t *context) {
if (--context->ref_count == 0) {
us_internal_socket_context_free(ssl, context);
}
}
void us_socket_context_free(int ssl, struct us_socket_context_t *context) {
us_socket_context_unref(ssl, context);
}
struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context, const char *host, int port, int options, int socket_ext_size) {
@@ -703,13 +726,15 @@ struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_con
#endif
/* Cannot adopt a closed socket */
if (us_socket_is_closed(ssl, s)) {
if (us_socket_is_closed(ssl, s) || us_socket_is_shut_down(ssl, s)) {
return s;
}
if (s->low_prio_state != 1) {
/* We need to be sure that we still holding a reference*/
us_socket_context_ref(ssl, context);
/* This properly updates the iterator if in on_timeout */
us_internal_socket_context_unlink_socket(s->context, s);
us_internal_socket_context_unlink_socket(ssl, s->context, s);
}
@@ -734,6 +759,7 @@ struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_con
if (new_s->next) new_s->next->prev = new_s;
} else {
us_internal_socket_context_link_socket(context, new_s);
us_socket_context_unref(ssl, context);
}
return new_s;

View File

@@ -14,8 +14,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// clang-format off
#if (defined(LIBUS_USE_OPENSSL) || defined(LIBUS_USE_WOLFSSL))
#include "internal/internal.h"
#include "libusockets.h"
#include <string.h>
/* These are in sni_tree.cpp */
void *sni_new();
void sni_free(void *sni, void (*cb)(void *));
@@ -23,10 +29,6 @@ int sni_add(void *sni, const char *hostname, void *user);
void *sni_remove(void *sni, const char *hostname);
void *sni_find(void *sni, const char *hostname);
#include "internal/internal.h"
#include "libusockets.h"
#include <string.h>
/* This module contains the entire OpenSSL implementation
* of the SSL socket and socket context interfaces. */
#ifdef LIBUS_USE_OPENSSL
@@ -71,10 +73,6 @@ struct us_internal_ssl_socket_context_t {
// socket context
SSL_CTX *ssl_context;
int is_parent;
#if ALLOW_SERVER_RENEGOTIATION
unsigned int client_renegotiation_limit;
unsigned int client_renegotiation_window;
#endif
/* These decorate the base implementation */
struct us_internal_ssl_socket_t *(*on_open)(struct us_internal_ssl_socket_t *,
int is_client, char *ip,
@@ -86,6 +84,10 @@ struct us_internal_ssl_socket_context_t {
struct us_internal_ssl_socket_t *(*on_close)(
struct us_internal_ssl_socket_t *, int code, void *reason);
struct us_internal_ssl_socket_t *(*on_timeout)(
struct us_internal_ssl_socket_t *);
struct us_internal_ssl_socket_t *(*on_long_timeout)(struct us_internal_ssl_socket_t *);
/* Called for missing SNI hostnames, if not NULL */
void (*on_server_name)(struct us_internal_ssl_socket_context_t *,
const char *hostname);
@@ -108,15 +110,10 @@ enum {
struct us_internal_ssl_socket_t {
struct us_socket_t s;
SSL *ssl; // this _must_ be the first member after s
#if ALLOW_SERVER_RENEGOTIATION
unsigned int client_pending_renegotiations;
uint64_t last_ssl_renegotiation;
unsigned int is_client : 1;
#endif
unsigned int ssl_write_wants_read : 1; // we use this for now
unsigned int ssl_read_wants_write : 1;
unsigned int handshake_state : 2;
unsigned int received_ssl_shutdown : 1;
unsigned int fatal_error : 1;
};
int passphrase_cb(char *buf, int size, int rwflag, void *u) {
@@ -182,10 +179,9 @@ int BIO_s_custom_read(BIO *bio, char *dst, int length) {
return length;
}
struct us_internal_ssl_socket_t *ssl_on_open(struct us_internal_ssl_socket_t *s,
int is_client, char *ip,
int ip_length) {
struct loop_ssl_data * us_internal_set_loop_ssl_data(struct us_internal_ssl_socket_t *s) {
// note: this context can change when we adopt the socket!
struct us_internal_ssl_socket_context_t *context =
(struct us_internal_ssl_socket_context_t *)us_socket_context(0, &s->s);
@@ -193,17 +189,31 @@ struct us_internal_ssl_socket_t *ssl_on_open(struct us_internal_ssl_socket_t *s,
struct loop_ssl_data *loop_ssl_data =
(struct loop_ssl_data *)loop->data.ssl_data;
s->ssl = SSL_new(context->ssl_context);
#if ALLOW_SERVER_RENEGOTIATION
s->client_pending_renegotiations = context->client_renegotiation_limit;
s->last_ssl_renegotiation = 0;
s->is_client = is_client ? 1 : 0;
// note: if we put data here we should never really clear it (not in write
// either, it still should be available for SSL_write to read from!)
#endif
loop_ssl_data->ssl_read_input_length = 0;
loop_ssl_data->ssl_read_input_offset = 0;
loop_ssl_data->ssl_socket = &s->s;
loop_ssl_data->msg_more = 0;
return loop_ssl_data;
}
struct us_internal_ssl_socket_t *ssl_on_open(struct us_internal_ssl_socket_t *s,
int is_client, char *ip,
int ip_length) {
struct us_internal_ssl_socket_context_t *context =
(struct us_internal_ssl_socket_context_t *)us_socket_context(0, &s->s);
struct loop_ssl_data *loop_ssl_data = us_internal_set_loop_ssl_data(s);
s->ssl = SSL_new(context->ssl_context);
s->ssl_write_wants_read = 0;
s->ssl_read_wants_write = 0;
s->fatal_error = 0;
s->handshake_state = HANDSHAKE_PENDING;
s->received_ssl_shutdown = 0;
SSL_set_bio(s->ssl, loop_ssl_data->shared_rbio, loop_ssl_data->shared_wbio);
// if we allow renegotiation, we need to set the mode here
@@ -213,24 +223,18 @@ struct us_internal_ssl_socket_t *ssl_on_open(struct us_internal_ssl_socket_t *s,
// this can be a DoS vector for servers, so we enable it using a limit
// we do not use ssl_renegotiate_freely, since ssl_renegotiate_explicit is
// more performant when using BoringSSL
#if ALLOW_SERVER_RENEGOTIATION
if (context->client_renegotiation_limit) {
SSL_set_renegotiate_mode(s->ssl, ssl_renegotiate_explicit);
} else {
SSL_set_renegotiate_mode(s->ssl, ssl_renegotiate_never);
}
#endif
BIO_up_ref(loop_ssl_data->shared_rbio);
BIO_up_ref(loop_ssl_data->shared_wbio);
if (is_client) {
#if ALLOW_SERVER_RENEGOTIATION == 0
SSL_set_renegotiate_mode(s->ssl, ssl_renegotiate_explicit);
#endif
SSL_set_connect_state(s->ssl);
} else {
SSL_set_accept_state(s->ssl);
// we do not allow renegotiation on the server side (should be the default for BoringSSL, but we set to make openssl compatible)
SSL_set_renegotiate_mode(s->ssl, ssl_renegotiate_never);
}
struct us_internal_ssl_socket_t *result =
@@ -246,6 +250,43 @@ struct us_internal_ssl_socket_t *ssl_on_open(struct us_internal_ssl_socket_t *s,
return result;
}
/// @brief Complete the shutdown or do a fast shutdown when needed, this should only be called before closing the socket
/// @param s
int us_internal_handle_shutdown(struct us_internal_ssl_socket_t *s, int force_fast_shutdown) {
// if we are already shutdown or in the middle of a handshake we dont need to do anything
if(us_internal_ssl_socket_is_shut_down(s) || s->fatal_error) return 1;
// we are closing the socket but did not sent a shutdown yet
int state = SSL_get_shutdown(s->ssl);
int sent_shutdown = state & SSL_SENT_SHUTDOWN;
int received_shutdown = state & SSL_RECEIVED_SHUTDOWN;
// if we are missing a shutdown call, we need to do a fast shutdown here
if(!sent_shutdown || !received_shutdown) {
// make sure that the ssl loop data is set
us_internal_set_loop_ssl_data(s);
// Zero means that we should wait for the peer to close the connection
// but we are already closing the connection so we do a fast shutdown here
int ret = SSL_shutdown(s->ssl);
if(ret == 0 && force_fast_shutdown) {
// do a fast shutdown (dont wait for peer)
ret = SSL_shutdown(s->ssl);
}
if(ret < 0) {
// we got some error here, but we dont care about it, we are closing the socket
int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
// clear
ERR_clear_error();
s->fatal_error = 1;
return 1;
}
}
return ret == 1;
}
return 1;
}
void us_internal_on_ssl_handshake(
struct us_internal_ssl_socket_context_t *context,
void (*on_handshake)(struct us_internal_ssl_socket_t *, int success,
@@ -256,9 +297,17 @@ void us_internal_on_ssl_handshake(
context->handshake_data = custom_data;
}
int us_internal_ssl_socket_is_closed(struct us_internal_ssl_socket_t *s) {
return us_socket_is_closed(0, &s->s);
}
struct us_internal_ssl_socket_t *
us_internal_ssl_socket_close(struct us_internal_ssl_socket_t *s, int code,
void *reason) {
// check if we are already closed
if (us_internal_ssl_socket_is_closed(s)) return s;
if (s->handshake_state != HANDSHAKE_COMPLETED) {
// if we have some pending handshake we cancel it and try to check the
// latest handshake error this way we will always call on_handshake with the
@@ -269,8 +318,14 @@ us_internal_ssl_socket_close(struct us_internal_ssl_socket_t *s, int code,
us_internal_trigger_handshake_callback(s, 0);
}
return (struct us_internal_ssl_socket_t *)us_socket_close(
0, (struct us_socket_t *)s, code, reason);
// if we are in the middle of a close_notify we need to finish it (code != 0 forces a fast shutdown)
int can_close = s->ssl && us_internal_handle_shutdown(s, code != 0);
// only close the socket if we are not in the middle of a handshake
if(can_close) {
return (struct us_internal_ssl_socket_t *)us_socket_close(0, (struct us_socket_t *)s, code, reason);
}
return s;
}
void us_internal_trigger_handshake_callback(struct us_internal_ssl_socket_t *s,
@@ -292,26 +347,7 @@ int us_internal_ssl_renegotiate(struct us_internal_ssl_socket_t *s) {
// if is a server and we have no pending renegotiation we can check
// the limits
s->handshake_state = HANDSHAKE_RENEGOTIATION_PENDING;
#if ALLOW_SERVER_RENEGOTIATION
if (!s->is_client && !SSL_renegotiate_pending(s->ssl)) {
uint64_t now = time(NULL);
struct us_internal_ssl_socket_context_t *context =
(struct us_internal_ssl_socket_context_t *)us_socket_context(0, &s->s);
// if is not the first time we negotiate and we are outside the time
// window, reset the limits
if (s->last_ssl_renegotiation && (now - s->last_ssl_renegotiation) >=
context->client_renegotiation_window) {
// reset the limits
s->client_pending_renegotiations = context->client_renegotiation_limit;
}
// if we have no more renegotiations, we should close the connection
if (s->client_pending_renegotiations == 0) {
return 0;
}
s->last_ssl_renegotiation = now;
s->client_pending_renegotiations--;
}
#endif
if (!SSL_renegotiate(s->ssl)) {
// we failed to renegotiate
us_internal_trigger_handshake_callback(s, 0);
@@ -321,24 +357,13 @@ int us_internal_ssl_renegotiate(struct us_internal_ssl_socket_t *s) {
}
void us_internal_update_handshake(struct us_internal_ssl_socket_t *s) {
struct us_internal_ssl_socket_context_t *context =
(struct us_internal_ssl_socket_context_t *)us_socket_context(0, &s->s);
// nothing todo here, renegotiation must be handled in SSL_read
if (s->handshake_state != HANDSHAKE_PENDING)
return;
struct us_loop_t *loop = us_socket_context_loop(0, &context->sc);
struct loop_ssl_data *loop_ssl_data =
(struct loop_ssl_data *)loop->data.ssl_data;
loop_ssl_data->ssl_read_input_length = 0;
loop_ssl_data->ssl_read_input_offset = 0;
loop_ssl_data->ssl_socket = &s->s;
loop_ssl_data->msg_more = 0;
if (us_socket_is_closed(0, &s->s) || us_internal_ssl_socket_is_shut_down(s) ||
SSL_get_shutdown(s->ssl) & SSL_RECEIVED_SHUTDOWN) {
if (us_internal_ssl_socket_is_closed(s) || us_internal_ssl_socket_is_shut_down(s) ||
(s->ssl && SSL_get_shutdown(s->ssl) & SSL_RECEIVED_SHUTDOWN)) {
us_internal_trigger_handshake_callback(s, 0);
return;
@@ -347,7 +372,6 @@ void us_internal_update_handshake(struct us_internal_ssl_socket_t *s) {
int result = SSL_do_handshake(s->ssl);
if (SSL_get_shutdown(s->ssl) & SSL_RECEIVED_SHUTDOWN) {
s->received_ssl_shutdown = 1;
us_internal_ssl_socket_close(s, 0, NULL);
return;
}
@@ -356,30 +380,23 @@ void us_internal_update_handshake(struct us_internal_ssl_socket_t *s) {
int err = SSL_get_error(s->ssl, result);
// as far as I know these are the only errors we want to handle
if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
us_internal_trigger_handshake_callback(s, 1);
// clear per thread error queue if it may contain something
if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
ERR_clear_error();
s->fatal_error = 1;
}
us_internal_trigger_handshake_callback(s, 0);
return;
}
s->handshake_state = HANDSHAKE_PENDING;
// Ensure that we'll cycle through internal openssl's state
if (!us_socket_is_closed(0, &s->s) &&
!us_internal_ssl_socket_is_shut_down(s)) {
us_socket_write(1, loop_ssl_data->ssl_socket, "\0", 0, 0);
}
s->ssl_write_wants_read = 1;
return;
}
// success
us_internal_trigger_handshake_callback(s, 1);
// Ensure that we'll cycle through internal openssl's state
if (!us_socket_is_closed(0, &s->s) &&
!us_internal_ssl_socket_is_shut_down(s)) {
us_socket_write(1, loop_ssl_data->ssl_socket, "\0", 0, 0);
}
s->ssl_write_wants_read = 1;
}
struct us_internal_ssl_socket_t *
@@ -387,16 +404,33 @@ ssl_on_close(struct us_internal_ssl_socket_t *s, int code, void *reason) {
struct us_internal_ssl_socket_context_t *context =
(struct us_internal_ssl_socket_context_t *)us_socket_context(0, &s->s);
SSL_free(s->ssl);
us_internal_set_loop_ssl_data(s);
struct us_internal_ssl_socket_t * ret = context->on_close(s, code, reason);
SSL_free(s->ssl); // free SSL after on_close
s->ssl = NULL; // set to NULL
return ret;
}
return context->on_close(s, code, reason);
struct us_internal_ssl_socket_t * ssl_on_timeout(struct us_internal_ssl_socket_t *s) {
struct us_internal_ssl_socket_context_t *context =
(struct us_internal_ssl_socket_context_t *)us_socket_context(0, &s->s);
us_internal_set_loop_ssl_data(s);
return context->on_timeout(s);
}
struct us_internal_ssl_socket_t * ssl_on_long_timeout(struct us_internal_ssl_socket_t *s) {
struct us_internal_ssl_socket_context_t *context =
(struct us_internal_ssl_socket_context_t *)us_socket_context(0, &s->s);
us_internal_set_loop_ssl_data(s);
return context->on_long_timeout(s);
}
struct us_internal_ssl_socket_t *
ssl_on_end(struct us_internal_ssl_socket_t *s) {
us_internal_set_loop_ssl_data(s);
// whatever state we are in, a TCP FIN is always an answered shutdown
/* Todo: this should report CLEANLY SHUTDOWN as reason */
return us_internal_ssl_socket_close(s, 0, NULL);
}
@@ -408,43 +442,20 @@ struct us_internal_ssl_socket_t *ssl_on_data(struct us_internal_ssl_socket_t *s,
struct us_internal_ssl_socket_context_t *context =
(struct us_internal_ssl_socket_context_t *)us_socket_context(0, &s->s);
struct us_loop_t *loop = us_socket_context_loop(0, &context->sc);
struct loop_ssl_data *loop_ssl_data =
(struct loop_ssl_data *)loop->data.ssl_data;
struct loop_ssl_data *loop_ssl_data = us_internal_set_loop_ssl_data(s);
// note: if we put data here we should never really clear it (not in write
// either, it still should be available for SSL_write to read from!)
loop_ssl_data->ssl_read_input = data;
loop_ssl_data->ssl_read_input_length = length;
loop_ssl_data->ssl_read_input_offset = 0;
loop_ssl_data->ssl_socket = &s->s;
loop_ssl_data->msg_more = 0;
if (us_socket_is_closed(0, &s->s) || s->received_ssl_shutdown) {
if (us_internal_ssl_socket_is_closed(s)) {
return NULL;
}
if (us_internal_ssl_socket_is_shut_down(s)) {
int ret = 0;
if ((ret = SSL_shutdown(s->ssl)) == 1) {
// two phase shutdown is complete here
/* Todo: this should also report some kind of clean shutdown */
return us_internal_ssl_socket_close(s, 0, NULL);
} else if (ret < 0) {
int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
// we need to clear the error queue in case these added to the thread
// local queue
ERR_clear_error();
}
}
// no further processing of data when in shutdown state
return s;
us_internal_ssl_socket_close(s, 0, NULL);
return NULL;
}
// bug checking: this loop needs a lot of attention and clean-ups and
@@ -452,17 +463,12 @@ struct us_internal_ssl_socket_t *ssl_on_data(struct us_internal_ssl_socket_t *s,
int read = 0;
restart:
// read until shutdown
while (!s->received_ssl_shutdown) {
while (1) {
int just_read = SSL_read(s->ssl,
loop_ssl_data->ssl_read_output +
LIBUS_RECV_BUFFER_PADDING + read,
LIBUS_RECV_BUFFER_LENGTH - read);
// we need to check if we received a shutdown here
if (SSL_get_shutdown(s->ssl) & SSL_RECEIVED_SHUTDOWN) {
s->received_ssl_shutdown = 1;
// we will only close after we handle the data and errors
}
if (just_read <= 0) {
int err = SSL_get_error(s->ssl, just_read);
// as far as I know these are the only errors we want to handle
@@ -477,8 +483,9 @@ restart:
// clean and close renegotiation failed
err = SSL_ERROR_SSL;
} else if (err == SSL_ERROR_ZERO_RETURN) {
// zero return can be EOF/FIN, if we have data just signal on_data and
// close
// Remotely-Initiated Shutdown
// See: https://www.openssl.org/docs/manmaster/man3/SSL_shutdown.html
if (read) {
context =
(struct us_internal_ssl_socket_context_t *)us_socket_context(
@@ -487,21 +494,24 @@ restart:
s = context->on_data(
s, loop_ssl_data->ssl_read_output + LIBUS_RECV_BUFFER_PADDING,
read);
if (!s || us_socket_is_closed(0, &s->s)) {
return s;
if (!s || us_internal_ssl_socket_is_closed(s)) {
return NULL; // stop processing data
}
}
// terminate connection here
return us_internal_ssl_socket_close(s, 0, NULL);
us_internal_ssl_socket_close(s, 0, NULL);
return NULL; // stop processing data
}
if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
// clear per thread error queue if it may contain something
ERR_clear_error();
s->fatal_error = 1;
}
// terminate connection here
return us_internal_ssl_socket_close(s, 0, NULL);
us_internal_ssl_socket_close(s, 0, NULL);
return NULL; // stop processing data
} else {
// emit the data we have and exit
@@ -526,8 +536,8 @@ restart:
s = context->on_data(
s, loop_ssl_data->ssl_read_output + LIBUS_RECV_BUFFER_PADDING,
read);
if (!s || us_socket_is_closed(0, &s->s)) {
return s;
if (!s || us_internal_ssl_socket_is_closed(s)) {
return NULL; // stop processing data
}
break;
@@ -549,20 +559,14 @@ restart:
// emit data and restart
s = context->on_data(
s, loop_ssl_data->ssl_read_output + LIBUS_RECV_BUFFER_PADDING, read);
if (!s || us_socket_is_closed(0, &s->s)) {
return s;
if (!s || us_internal_ssl_socket_is_closed(s)) {
return NULL;
}
read = 0;
goto restart;
}
}
// we received the shutdown after reading so we close
if (s->received_ssl_shutdown) {
us_internal_ssl_socket_close(s, 0, NULL);
return NULL;
}
// trigger writable if we failed last write with want read
if (s->ssl_write_wants_read) {
s->ssl_write_wants_read = 0;
@@ -575,8 +579,8 @@ restart:
s = (struct us_internal_ssl_socket_t *)context->sc.on_writable(
&s->s); // cast here!
// if we are closed here, then exit
if (!s || us_socket_is_closed(0, &s->s)) {
return s;
if (!s || us_internal_ssl_socket_is_closed(s)) {
return NULL;
}
}
@@ -585,6 +589,7 @@ restart:
struct us_internal_ssl_socket_t *
ssl_on_writable(struct us_internal_ssl_socket_t *s) {
us_internal_set_loop_ssl_data(s);
us_internal_update_handshake(s);
struct us_internal_ssl_socket_context_t *context =
@@ -606,8 +611,8 @@ ssl_on_writable(struct us_internal_ssl_socket_t *s) {
}
// Do not call on_writable if the socket is closed.
// on close means the socket data is no longer accessible
if (!s || us_socket_is_closed(0, &s->s)) {
return 0;
if (!s || us_internal_ssl_socket_is_closed(s) || us_internal_ssl_socket_is_shut_down(s)) {
return s;
}
if (s->handshake_state == HANDSHAKE_COMPLETED) {
@@ -1032,7 +1037,7 @@ long us_internal_verify_peer_certificate( // NOLINT(runtime/int)
struct us_bun_verify_error_t
us_internal_verify_error(struct us_internal_ssl_socket_t *s) {
if (us_socket_is_closed(0, &s->s) || us_internal_ssl_socket_is_shut_down(s)) {
if (us_internal_ssl_socket_is_closed(s) || us_internal_ssl_socket_is_shut_down(s)) {
return (struct us_bun_verify_error_t){
.error = 0, .code = NULL, .reason = NULL};
}
@@ -1317,10 +1322,6 @@ void us_bun_internal_ssl_socket_context_add_server_name(
/* We do not want to hold any nullptr's in our SNI tree */
if (ssl_context) {
#if ALLOW_SERVER_RENEGOTIATION
context->client_renegotiation_limit = options.client_renegotiation_limit;
context->client_renegotiation_window = options.client_renegotiation_window;
#endif
if (sni_add(context->sni, hostname_pattern, ssl_context)) {
/* If we already had that name, ignore */
free_ssl_context(ssl_context);
@@ -1469,10 +1470,6 @@ us_internal_bun_create_ssl_socket_context(
context->on_handshake = NULL;
context->handshake_data = NULL;
#if ALLOW_SERVER_RENEGOTIATION
context->client_renegotiation_limit = options.client_renegotiation_limit;
context->client_renegotiation_window = options.client_renegotiation_window;
#endif
/* We, as parent context, may ignore data */
context->sc.is_low_prio = (int (*)(struct us_socket_t *))ssl_is_low_prio;
@@ -1503,7 +1500,7 @@ void us_internal_ssl_socket_context_free(
sni_free(context->sni, sni_hostname_destructor);
}
us_socket_context_free(0, &context->sc);
us_internal_socket_context_free(0, &context->sc);
}
struct us_listen_socket_t *us_internal_ssl_socket_context_listen(
@@ -1592,7 +1589,8 @@ void us_internal_ssl_socket_context_on_timeout(
struct us_internal_ssl_socket_t *s)) {
us_socket_context_on_timeout(0, (struct us_socket_context_t *)context,
(struct us_socket_t * (*)(struct us_socket_t *))
on_timeout);
ssl_on_timeout);
context->on_timeout = on_timeout;
}
void us_internal_ssl_socket_context_on_long_timeout(
@@ -1601,7 +1599,8 @@ void us_internal_ssl_socket_context_on_long_timeout(
struct us_internal_ssl_socket_t *s)) {
us_socket_context_on_long_timeout(
0, (struct us_socket_context_t *)context,
(struct us_socket_t * (*)(struct us_socket_t *)) on_long_timeout);
(struct us_socket_t * (*)(struct us_socket_t *)) ssl_on_long_timeout);
context->on_long_timeout = on_long_timeout;
}
/* We do not really listen to passed FIN-handler, we entirely override it with
@@ -1656,8 +1655,8 @@ int us_internal_ssl_socket_raw_write(struct us_internal_ssl_socket_t *s,
int us_internal_ssl_socket_write(struct us_internal_ssl_socket_t *s,
const char *data, int length, int msg_more) {
if (us_socket_is_closed(0, &s->s) || us_internal_ssl_socket_is_shut_down(s)) {
if (us_socket_is_closed(0, &s->s) || us_internal_ssl_socket_is_shut_down(s) || length == 0) {
return 0;
}
@@ -1697,6 +1696,7 @@ int us_internal_ssl_socket_write(struct us_internal_ssl_socket_t *s,
// these two errors may add to the error queue, which is per thread and
// must be cleared
ERR_clear_error();
s->fatal_error = 1;
// all errors here except for want write are critical and should not
// happen
@@ -1714,12 +1714,12 @@ void *us_internal_connecting_ssl_socket_ext(struct us_connecting_socket_t *s) {
}
int us_internal_ssl_socket_is_shut_down(struct us_internal_ssl_socket_t *s) {
return us_socket_is_shut_down(0, &s->s) ||
SSL_get_shutdown(s->ssl) & SSL_SENT_SHUTDOWN;
return !s->ssl || us_socket_is_shut_down(0, &s->s) ||
SSL_get_shutdown(s->ssl) & SSL_SENT_SHUTDOWN || s->fatal_error;
}
void us_internal_ssl_socket_shutdown(struct us_internal_ssl_socket_t *s) {
if (!us_socket_is_closed(0, &s->s) &&
if (!us_internal_ssl_socket_is_closed(s) &&
!us_internal_ssl_socket_is_shut_down(s)) {
struct us_internal_ssl_socket_context_t *context =
(struct us_internal_ssl_socket_context_t *)us_socket_context(0, &s->s);
@@ -1740,19 +1740,22 @@ void us_internal_ssl_socket_shutdown(struct us_internal_ssl_socket_t *s) {
loop_ssl_data->ssl_socket = &s->s;
loop_ssl_data->msg_more = 0;
// sets SSL_SENT_SHUTDOWN no matter what (not actually true if error!)
// sets SSL_SENT_SHUTDOWN and waits for the other side to do the same
int ret = SSL_shutdown(s->ssl);
if (ret == 0) {
ret = SSL_shutdown(s->ssl);
if (SSL_in_init(s->ssl) || SSL_get_quiet_shutdown(s->ssl)) {
// when SSL_in_init or quiet shutdown in BoringSSL, we call shutdown
// directly
us_socket_shutdown(0, &s->s);
return;
}
if (ret < 0) {
int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
// clear
ERR_clear_error();
s->fatal_error = 1;
}
// we get here if we are shutting down while still in init
@@ -2043,8 +2046,8 @@ us_socket_context_on_socket_connect_error(
socket->ssl = NULL;
socket->ssl_write_wants_read = 0;
socket->ssl_read_wants_write = 0;
socket->fatal_error = 0;
socket->handshake_state = HANDSHAKE_PENDING;
socket->received_ssl_shutdown = 0;
return socket;
}

View File

@@ -14,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// clang-format off
#pragma once
#ifndef INTERNAL_H
#define INTERNAL_H
@@ -100,6 +101,9 @@ struct addrinfo_result {
int error;
};
#define us_internal_ssl_socket_context_r struct us_internal_ssl_socket_context_t *nonnull_arg
#define us_internal_ssl_socket_r struct us_internal_ssl_socket_t *nonnull_arg
extern int Bun__addrinfo_get(struct us_loop_t* loop, const char* host, struct addrinfo_request** ptr);
extern int Bun__addrinfo_set(struct addrinfo_request* ptr, struct us_connecting_socket_t* socket);
extern void Bun__addrinfo_freeRequest(struct addrinfo_request* addrinfo_req, int error);
@@ -109,19 +113,19 @@ extern struct addrinfo_result *Bun__addrinfo_getRequestResult(struct addrinfo_re
/* Loop related */
void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error,
int events);
void us_internal_timer_sweep(struct us_loop_t *loop);
void us_internal_free_closed_sockets(struct us_loop_t *loop);
void us_internal_timer_sweep(us_loop_r loop);
void us_internal_free_closed_sockets(us_loop_r loop);
void us_internal_loop_link(struct us_loop_t *loop,
struct us_socket_context_t *context);
void us_internal_loop_unlink(struct us_loop_t *loop,
struct us_socket_context_t *context);
void us_internal_loop_data_init(struct us_loop_t *loop,
void (*wakeup_cb)(struct us_loop_t *loop),
void (*pre_cb)(struct us_loop_t *loop),
void (*post_cb)(struct us_loop_t *loop));
void us_internal_loop_data_free(struct us_loop_t *loop);
void us_internal_loop_pre(struct us_loop_t *loop);
void us_internal_loop_post(struct us_loop_t *loop);
void (*wakeup_cb)(us_loop_r loop),
void (*pre_cb)(us_loop_r loop),
void (*post_cb)(us_loop_r loop));
void us_internal_loop_data_free(us_loop_r loop);
void us_internal_loop_pre(us_loop_r loop);
void us_internal_loop_post(us_loop_r loop);
/* Asyncs (old) */
struct us_internal_async *us_internal_create_async(struct us_loop_t *loop,
@@ -138,18 +142,22 @@ int us_internal_poll_type(struct us_poll_t *p);
void us_internal_poll_set_type(struct us_poll_t *p, int poll_type);
/* SSL loop data */
void us_internal_init_loop_ssl_data(struct us_loop_t *loop);
void us_internal_free_loop_ssl_data(struct us_loop_t *loop);
void us_internal_init_loop_ssl_data(us_loop_r loop);
void us_internal_free_loop_ssl_data(us_loop_r loop);
/* Socket context related */
void us_internal_socket_context_link_socket(struct us_socket_context_t *context,
struct us_socket_t *s);
void us_internal_socket_context_unlink_socket(
struct us_socket_context_t *context, struct us_socket_t *s);
void us_internal_socket_context_link_socket(us_socket_context_r context,
us_socket_r s);
void us_internal_socket_context_unlink_socket(int ssl,
us_socket_context_r context, us_socket_r s);
void us_internal_socket_after_resolve(struct us_connecting_socket_t *s);
void us_internal_socket_after_open(struct us_socket_t *s, int error);
int us_internal_handle_dns_results(struct us_loop_t *loop);
void us_internal_socket_after_open(us_socket_r s, int error);
struct us_internal_ssl_socket_t *
us_internal_ssl_socket_close(us_internal_ssl_socket_r s, int code,
void *reason);
int us_internal_handle_dns_results(us_loop_r loop);
/* Sockets are polls */
struct us_socket_t {
@@ -243,13 +251,14 @@ struct us_listen_socket_t {
/* Listen sockets are keps in their own list */
void us_internal_socket_context_link_listen_socket(
struct us_socket_context_t *context, struct us_listen_socket_t *s);
void us_internal_socket_context_unlink_listen_socket(
struct us_socket_context_t *context, struct us_listen_socket_t *s);
us_socket_context_r context, struct us_listen_socket_t *s);
void us_internal_socket_context_unlink_listen_socket(int ssl,
us_socket_context_r context, struct us_listen_socket_t *s);
struct us_socket_context_t {
alignas(LIBUS_EXT_ALIGNMENT) struct us_loop_t *loop;
uint32_t global_tick;
uint32_t ref_count;
unsigned char timestamp;
unsigned char long_timestamp;
struct us_socket_t *head_sockets;
@@ -280,34 +289,35 @@ struct us_internal_ssl_socket_t;
typedef void (*us_internal_on_handshake_t)(
struct us_internal_ssl_socket_t *, int success,
struct us_bun_verify_error_t verify_error, void *custom_data);
void us_internal_socket_context_free(int ssl, struct us_socket_context_t *context);
/* SNI functions */
void us_internal_ssl_socket_context_add_server_name(
struct us_internal_ssl_socket_context_t *context,
us_internal_ssl_socket_context_r context,
const char *hostname_pattern, struct us_socket_context_options_t options,
void *user);
void us_bun_internal_ssl_socket_context_add_server_name(
struct us_internal_ssl_socket_context_t *context,
us_internal_ssl_socket_context_r context,
const char *hostname_pattern,
struct us_bun_socket_context_options_t options, void *user);
void us_internal_ssl_socket_context_remove_server_name(
struct us_internal_ssl_socket_context_t *context,
us_internal_ssl_socket_context_r context,
const char *hostname_pattern);
void us_internal_ssl_socket_context_on_server_name(
struct us_internal_ssl_socket_context_t *context,
us_internal_ssl_socket_context_r context,
void (*cb)(struct us_internal_ssl_socket_context_t *, const char *));
void *
us_internal_ssl_socket_get_sni_userdata(struct us_internal_ssl_socket_t *s);
us_internal_ssl_socket_get_sni_userdata(us_internal_ssl_socket_r s);
void *us_internal_ssl_socket_context_find_server_name_userdata(
struct us_internal_ssl_socket_context_t *context,
us_internal_ssl_socket_context_r context,
const char *hostname_pattern);
void *
us_internal_ssl_socket_get_native_handle(struct us_internal_ssl_socket_t *s);
us_internal_ssl_socket_get_native_handle(us_internal_ssl_socket_r s);
void *us_internal_ssl_socket_context_get_native_handle(
struct us_internal_ssl_socket_context_t *context);
us_internal_ssl_socket_context_r context);
struct us_bun_verify_error_t
us_internal_verify_error(struct us_internal_ssl_socket_t *s);
us_internal_verify_error(us_internal_ssl_socket_r s);
struct us_internal_ssl_socket_context_t *us_internal_create_ssl_socket_context(
struct us_loop_t *loop, int context_ext_size,
struct us_socket_context_options_t options);
@@ -317,108 +327,109 @@ us_internal_bun_create_ssl_socket_context(
struct us_bun_socket_context_options_t options);
void us_internal_ssl_socket_context_free(
struct us_internal_ssl_socket_context_t *context);
us_internal_ssl_socket_context_r context);
void us_internal_ssl_socket_context_on_open(
struct us_internal_ssl_socket_context_t *context,
us_internal_ssl_socket_context_r context,
struct us_internal_ssl_socket_t *(*on_open)(
struct us_internal_ssl_socket_t *s, int is_client, char *ip,
us_internal_ssl_socket_r s, int is_client, char *ip,
int ip_length));
void us_internal_ssl_socket_context_on_close(
struct us_internal_ssl_socket_context_t *context,
us_internal_ssl_socket_context_r context,
struct us_internal_ssl_socket_t *(*on_close)(
struct us_internal_ssl_socket_t *s, int code, void *reason));
us_internal_ssl_socket_r s, int code, void *reason));
void us_internal_ssl_socket_context_on_data(
struct us_internal_ssl_socket_context_t *context,
us_internal_ssl_socket_context_r context,
struct us_internal_ssl_socket_t *(*on_data)(
struct us_internal_ssl_socket_t *s, char *data, int length));
us_internal_ssl_socket_r s, char *data, int length));
void us_internal_update_handshake(struct us_internal_ssl_socket_t *s);
int us_internal_renegotiate(struct us_internal_ssl_socket_t *s);
void us_internal_trigger_handshake_callback(struct us_internal_ssl_socket_t *s,
void us_internal_update_handshake(us_internal_ssl_socket_r s);
int us_internal_renegotiate(us_internal_ssl_socket_r s);
void us_internal_trigger_handshake_callback(us_internal_ssl_socket_r s,
int success);
void us_internal_on_ssl_handshake(
struct us_internal_ssl_socket_context_t *context,
us_internal_ssl_socket_context_r context,
us_internal_on_handshake_t onhandshake, void *custom_data);
void us_internal_ssl_socket_context_on_writable(
struct us_internal_ssl_socket_context_t *context,
us_internal_ssl_socket_context_r context,
struct us_internal_ssl_socket_t *(*on_writable)(
struct us_internal_ssl_socket_t *s));
us_internal_ssl_socket_r s));
void us_internal_ssl_socket_context_on_timeout(
struct us_internal_ssl_socket_context_t *context,
us_internal_ssl_socket_context_r context,
struct us_internal_ssl_socket_t *(*on_timeout)(
struct us_internal_ssl_socket_t *s));
us_internal_ssl_socket_r s));
void us_internal_ssl_socket_context_on_long_timeout(
struct us_internal_ssl_socket_context_t *context,
us_internal_ssl_socket_context_r context,
struct us_internal_ssl_socket_t *(*on_timeout)(
struct us_internal_ssl_socket_t *s));
us_internal_ssl_socket_r s));
void us_internal_ssl_socket_context_on_end(
struct us_internal_ssl_socket_context_t *context,
us_internal_ssl_socket_context_r context,
struct us_internal_ssl_socket_t *(*on_end)(
struct us_internal_ssl_socket_t *s));
us_internal_ssl_socket_r s));
void us_internal_ssl_socket_context_on_connect_error(
struct us_internal_ssl_socket_context_t *context,
us_internal_ssl_socket_context_r context,
struct us_internal_ssl_socket_t *(*on_connect_error)(
struct us_internal_ssl_socket_t *s, int code));
us_internal_ssl_socket_r s, int code));
void us_internal_ssl_socket_context_on_socket_connect_error(
struct us_internal_ssl_socket_context_t *context,
us_internal_ssl_socket_context_r context,
struct us_internal_ssl_socket_t *(*on_socket_connect_error)(
struct us_internal_ssl_socket_t *s, int code));
us_internal_ssl_socket_r s, int code));
struct us_listen_socket_t *us_internal_ssl_socket_context_listen(
struct us_internal_ssl_socket_context_t *context, const char *host,
us_internal_ssl_socket_context_r context, const char *host,
int port, int options, int socket_ext_size);
struct us_listen_socket_t *us_internal_ssl_socket_context_listen_unix(
struct us_internal_ssl_socket_context_t *context, const char *path,
us_internal_ssl_socket_context_r context, const char *path,
size_t pathlen, int options, int socket_ext_size);
struct us_connecting_socket_t *us_internal_ssl_socket_context_connect(
struct us_internal_ssl_socket_context_t *context, const char *host,
us_internal_ssl_socket_context_r context, const char *host,
int port, int options, int socket_ext_size, int* is_resolved);
struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect_unix(
struct us_internal_ssl_socket_context_t *context, const char *server_path,
us_internal_ssl_socket_context_r context, const char *server_path,
size_t pathlen, int options, int socket_ext_size);
int us_internal_ssl_socket_write(struct us_internal_ssl_socket_t *s,
int us_internal_ssl_socket_write(us_internal_ssl_socket_r s,
const char *data, int length, int msg_more);
int us_internal_ssl_socket_raw_write(struct us_internal_ssl_socket_t *s,
int us_internal_ssl_socket_raw_write(us_internal_ssl_socket_r s,
const char *data, int length,
int msg_more);
void us_internal_ssl_socket_timeout(struct us_internal_ssl_socket_t *s,
void us_internal_ssl_socket_timeout(us_internal_ssl_socket_r s,
unsigned int seconds);
void *
us_internal_ssl_socket_context_ext(struct us_internal_ssl_socket_context_t *s);
struct us_internal_ssl_socket_context_t *
us_internal_ssl_socket_get_context(struct us_internal_ssl_socket_t *s);
void *us_internal_ssl_socket_ext(struct us_internal_ssl_socket_t *s);
us_internal_ssl_socket_get_context(us_internal_ssl_socket_r s);
void *us_internal_ssl_socket_ext(us_internal_ssl_socket_r s);
void *us_internal_connecting_ssl_socket_ext(struct us_connecting_socket_t *c);
int us_internal_ssl_socket_is_shut_down(struct us_internal_ssl_socket_t *s);
void us_internal_ssl_socket_shutdown(struct us_internal_ssl_socket_t *s);
int us_internal_ssl_socket_is_shut_down(us_internal_ssl_socket_r s);
int us_internal_ssl_socket_is_closed(us_internal_ssl_socket_r s);
void us_internal_ssl_socket_shutdown(us_internal_ssl_socket_r s);
struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_adopt_socket(
struct us_internal_ssl_socket_context_t *context,
struct us_internal_ssl_socket_t *s, int ext_size);
us_internal_ssl_socket_context_r context,
us_internal_ssl_socket_r s, int ext_size);
struct us_internal_ssl_socket_t *us_internal_ssl_socket_wrap_with_tls(
struct us_socket_t *s, struct us_bun_socket_context_options_t options,
us_socket_r s, struct us_bun_socket_context_options_t options,
struct us_socket_events_t events, int socket_ext_size);
struct us_internal_ssl_socket_context_t *
us_internal_create_child_ssl_socket_context(
struct us_internal_ssl_socket_context_t *context, int context_ext_size);
us_internal_ssl_socket_context_r context, int context_ext_size);
struct us_loop_t *us_internal_ssl_socket_context_loop(
struct us_internal_ssl_socket_context_t *context);
us_internal_ssl_socket_context_r context);
struct us_internal_ssl_socket_t *
us_internal_ssl_socket_open(struct us_internal_ssl_socket_t *s, int is_client,
us_internal_ssl_socket_open(us_internal_ssl_socket_r s, int is_client,
char *ip, int ip_length);
int us_raw_root_certs(struct us_cert_string_t **out);

View File

@@ -27,6 +27,7 @@ struct us_internal_loop_data_t {
int last_write_failed;
struct us_socket_context_t *head;
struct us_socket_context_t *iterator;
struct us_socket_context_t *closed_context_head;
char *recv_buf;
char *send_buf;
void *ssl_data;

View File

@@ -17,6 +17,7 @@
#ifndef BSD_H
#define BSD_H
#pragma once
// top-most wrapper of bsd-like syscalls
@@ -25,7 +26,7 @@
#include "libusockets.h"
#ifdef _WIN32
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
@@ -34,7 +35,7 @@
#pragma comment(lib, "ws2_32.lib")
#define SETSOCKOPT_PTR_TYPE const char *
#define LIBUS_SOCKET_ERROR INVALID_SOCKET
#else
#else /* POSIX */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
@@ -64,14 +65,76 @@ struct bsd_addr_t {
#endif
#ifdef __APPLE__
// a.k.a msghdr_x
struct mmsghdr {
/*
* Extended version for sendmsg_x() and recvmsg_x() calls
*/
struct mmsghdr {
struct msghdr msg_hdr;
size_t msg_len; /* byte length of buffer in msg_iov */
};
/*
* recvmsg_x() is a system call similar to recvmsg(2) to receive
* several datagrams at once in the array of message headers "msgp".
*
* recvmsg_x() can be used only with protocols handlers that have been specially
* modified to support sending and receiving several datagrams at once.
*
* The size of the array "msgp" is given by the argument "cnt".
*
* The "flags" arguments supports only the value MSG_DONTWAIT.
*
* Each member of "msgp" array is of type "struct msghdr_x".
*
* The "msg_iov" and "msg_iovlen" are input parameters that describe where to
* store a datagram in a scatter gather locations of buffers -- see recvmsg(2).
* On output the field "msg_datalen" gives the length of the received datagram.
*
* The field "msg_flags" must be set to zero on input. On output, "msg_flags"
* may have MSG_TRUNC set to indicate the trailing portion of the datagram was
* discarded because the datagram was larger than the buffer supplied.
* recvmsg_x() returns as soon as a datagram is truncated.
*
* recvmsg_x() may return with less than "cnt" datagrams received based on
* the low water mark and the amount of data pending in the socket buffer.
*
* recvmsg_x() returns the number of datagrams that have been received,
* or -1 if an error occurred.
*
* NOTE: This a private system call, the API is subject to change.
*/
ssize_t recvmsg_x(int s, const struct mmsghdr *msgp, u_int cnt, int flags);
ssize_t sendmsg_x(int s, struct mmsghdr *msgp, u_int cnt, int flags);
ssize_t recvmsg_x(int s, struct mmsghdr *msgp, u_int cnt, int flags);
/*
* sendmsg_x() is a system call similar to send(2) to send
* several datagrams at once in the array of message headers "msgp".
*
* sendmsg_x() can be used only with protocols handlers that have been specially
* modified to support sending and receiving several datagrams at once.
*
* The size of the array "msgp" is given by the argument "cnt".
*
* The "flags" arguments supports only the value MSG_DONTWAIT.
*
* Each member of "msgp" array is of type "struct msghdr_x".
*
* The "msg_iov" and "msg_iovlen" are input parameters that specify the
* data to be sent in a scatter gather locations of buffers -- see sendmsg(2).
*
* sendmsg_x() fails with EMSGSIZE if the sum of the length of the datagrams
* is greater than the high water mark.
*
* Address and ancillary data are not supported so the following fields
* must be set to zero on input:
* "msg_name", "msg_namelen", "msg_control" and "msg_controllen".
*
* The field "msg_flags" and "msg_datalen" must be set to zero on input.
*
* sendmsg_x() returns the number of datagrams that have been sent,
* or -1 if an error occurred.
*
* NOTE: This a private system call, the API is subject to change.
*/
ssize_t sendmsg_x(int s, const struct mmsghdr *msgp, u_int cnt, int flags);
#endif
struct udp_recvbuf {
@@ -95,8 +158,9 @@ struct udp_sendbuf {
void **addresses;
int num;
#else
int num;
char has_empty;
unsigned int has_empty : 1;
unsigned int has_addresses : 1;
unsigned int num;
struct mmsghdr msgvec[];
#endif
};

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
// clang-format off
#pragma once
#ifndef us_calloc
#define us_calloc calloc
#endif
@@ -35,6 +35,25 @@
#ifndef LIBUSOCKETS_H
#define LIBUSOCKETS_H
#ifdef BUN_DEBUG
#define nonnull_arg _Nonnull
#else
#define nonnull_arg
#endif
#ifdef BUN_DEBUG
#define nonnull_fn_decl
#else
#ifndef nonnull_fn_decl
#define nonnull_fn_decl __attribute__((nonnull))
#endif
#endif
#define us_loop_r struct us_loop_t *nonnull_arg
#define us_socket_r struct us_socket_t *nonnull_arg
#define us_poll_r struct us_poll_t *nonnull_arg
#define us_socket_context_r struct us_socket_context_t *nonnull_arg
/* 512kb shared receive buffer */
#define LIBUS_RECV_BUFFER_LENGTH 524288
@@ -49,6 +68,7 @@
#define LIBUS_EXT_ALIGNMENT 16
#define ALLOW_SERVER_RENEGOTIATION 0
#define LIBUS_SOCKET_CLOSE_CODE_CLEAN_SHUTDOWN 0
#define LIBUS_SOCKET_CLOSE_CODE_CONNECTION_RESET 1
/* Define what a socket descriptor is based on platform */
@@ -123,11 +143,11 @@ struct us_udp_packet_buffer_t *us_create_udp_packet_buffer();
/* Creates a (heavy-weight) UDP socket with a user space ring buffer. Again, this one is heavy weight and
* shoud be reused. One entire QUIC server can be implemented using only one single UDP socket so weight
* is not a concern as is the case for TCP sockets which are 1-to-1 with TCP connections. */
//struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*read_cb)(struct us_udp_socket_t *), unsigned short port);
//struct us_udp_socket_t *us_create_udp_socket(us_loop_r loop, void (*read_cb)(struct us_udp_socket_t *), unsigned short port);
//struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port);
//struct us_udp_socket_t *us_create_udp_socket(us_loop_r loop, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port);
struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*data_cb)(struct us_udp_socket_t *, void *, int), void (*drain_cb)(struct us_udp_socket_t *), void (*close_cb)(struct us_udp_socket_t *), const char *host, unsigned short port, void *user);
struct us_udp_socket_t *us_create_udp_socket(us_loop_r loop, void (*data_cb)(struct us_udp_socket_t *, void *, int), void (*drain_cb)(struct us_udp_socket_t *), void (*close_cb)(struct us_udp_socket_t *), const char *host, unsigned short port, void *user);
void us_udp_socket_close(struct us_udp_socket_t *s);
@@ -140,7 +160,7 @@ int us_udp_socket_bind(struct us_udp_socket_t *s, const char *hostname, unsigned
/* Public interfaces for timers */
/* Create a new high precision, low performance timer. May fail and return null */
struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size);
struct us_timer_t *us_create_timer(us_loop_r loop, int fallthrough, unsigned int ext_size);
/* Returns user data extension for this timer */
void *us_timer_ext(struct us_timer_t *timer);
@@ -174,17 +194,17 @@ struct us_bun_verify_error_t {
};
struct us_socket_events_t {
struct us_socket_t *(*on_open)(struct us_socket_t *, int is_client, char *ip, int ip_length);
struct us_socket_t *(*on_data)(struct us_socket_t *, char *data, int length);
struct us_socket_t *(*on_writable)(struct us_socket_t *);
struct us_socket_t *(*on_close)(struct us_socket_t *, int code, void *reason);
struct us_socket_t *(*on_open)(us_socket_r, int is_client, char *ip, int ip_length);
struct us_socket_t *(*on_data)(us_socket_r, char *data, int length);
struct us_socket_t *(*on_writable)(us_socket_r);
struct us_socket_t *(*on_close)(us_socket_r, int code, void *reason);
//void (*on_timeout)(struct us_socket_context *);
struct us_socket_t *(*on_timeout)(struct us_socket_t *);
struct us_socket_t *(*on_long_timeout)(struct us_socket_t *);
struct us_socket_t *(*on_end)(struct us_socket_t *);
struct us_socket_t *(*on_timeout)(us_socket_r);
struct us_socket_t *(*on_long_timeout)(us_socket_r);
struct us_socket_t *(*on_end)(us_socket_r);
struct us_connecting_socket_t *(*on_connect_error)(struct us_connecting_socket_t *, int code);
struct us_socket_t *(*on_connecting_socket_error)(struct us_socket_t *, int code);
void (*on_handshake)(struct us_socket_t*, int success, struct us_bun_verify_error_t verify_error, void* custom_data);
struct us_socket_t *(*on_connecting_socket_error)(us_socket_r, int code);
void (*on_handshake)(us_socket_r, int success, struct us_bun_verify_error_t verify_error, void* custom_data);
};
@@ -210,67 +230,70 @@ struct us_bun_socket_context_options_t {
};
/* Return 15-bit timestamp for this context */
unsigned short us_socket_context_timestamp(int ssl, struct us_socket_context_t *context);
unsigned short us_socket_context_timestamp(int ssl, us_socket_context_r context) nonnull_fn_decl;
/* Adds SNI domain and cert in asn1 format */
void us_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user);
void us_bun_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_bun_socket_context_options_t options, void *user);
void us_socket_context_remove_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern);
void us_socket_context_on_server_name(int ssl, struct us_socket_context_t *context, void (*cb)(struct us_socket_context_t *, const char *hostname));
void *us_socket_server_name_userdata(int ssl, struct us_socket_t *s);
void *us_socket_context_find_server_name_userdata(int ssl, struct us_socket_context_t *context, const char *hostname_pattern);
void us_socket_context_add_server_name(int ssl, us_socket_context_r context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user);
void us_bun_socket_context_add_server_name(int ssl, us_socket_context_r context, const char *hostname_pattern, struct us_bun_socket_context_options_t options, void *user);
void us_socket_context_remove_server_name(int ssl, us_socket_context_r context, const char *hostname_pattern);
void us_socket_context_on_server_name(int ssl, us_socket_context_r context, void (*cb)(us_socket_context_r context, const char *hostname));
void *us_socket_server_name_userdata(int ssl, us_socket_r s);
void *us_socket_context_find_server_name_userdata(int ssl, us_socket_context_r context, const char *hostname_pattern);
/* Returns the underlying SSL native handle, such as SSL_CTX or nullptr */
void *us_socket_context_get_native_handle(int ssl, struct us_socket_context_t *context);
void *us_socket_context_get_native_handle(int ssl, us_socket_context_r context);
/* A socket context holds shared callbacks and user data extension for associated sockets */
struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t *loop,
int ext_size, struct us_socket_context_options_t options);
struct us_socket_context_t *us_create_socket_context(int ssl, us_loop_r loop,
int ext_size, struct us_socket_context_options_t options) nonnull_fn_decl;
struct us_socket_context_t *us_create_bun_socket_context(int ssl, struct us_loop_t *loop,
int ext_size, struct us_bun_socket_context_options_t options);
int ext_size, struct us_bun_socket_context_options_t options) nonnull_fn_decl;
/* Delete resources allocated at creation time (will call unref now and only free when ref count == 0). */
void us_socket_context_free(int ssl, us_socket_context_r context) nonnull_fn_decl;
void us_socket_context_ref(int ssl, us_socket_context_r context) nonnull_fn_decl;
void us_socket_context_unref(int ssl, us_socket_context_r context) nonnull_fn_decl;
/* Delete resources allocated at creation time. */
void us_socket_context_free(int ssl, struct us_socket_context_t *context);
struct us_bun_verify_error_t us_socket_verify_error(int ssl, struct us_socket_t *context);
/* Setters of various async callbacks */
void us_socket_context_on_open(int ssl, struct us_socket_context_t *context,
struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length));
void us_socket_context_on_close(int ssl, struct us_socket_context_t *context,
struct us_socket_t *(*on_close)(struct us_socket_t *s, int code, void *reason));
void us_socket_context_on_data(int ssl, struct us_socket_context_t *context,
struct us_socket_t *(*on_data)(struct us_socket_t *s, char *data, int length));
void us_socket_context_on_writable(int ssl, struct us_socket_context_t *context,
struct us_socket_t *(*on_writable)(struct us_socket_t *s));
void us_socket_context_on_timeout(int ssl, struct us_socket_context_t *context,
struct us_socket_t *(*on_timeout)(struct us_socket_t *s));
void us_socket_context_on_long_timeout(int ssl, struct us_socket_context_t *context,
struct us_socket_t *(*on_timeout)(struct us_socket_t *s));
void us_socket_context_on_open(int ssl, us_socket_context_r context,
struct us_socket_t *(*on_open)(us_socket_r s, int is_client, char *ip, int ip_length));
void us_socket_context_on_close(int ssl, us_socket_context_r context,
struct us_socket_t *(*on_close)(us_socket_r s, int code, void *reason));
void us_socket_context_on_data(int ssl, us_socket_context_r context,
struct us_socket_t *(*on_data)(us_socket_r s, char *data, int length));
void us_socket_context_on_writable(int ssl, us_socket_context_r context,
struct us_socket_t *(*on_writable)(us_socket_r s));
void us_socket_context_on_timeout(int ssl, us_socket_context_r context,
struct us_socket_t *(*on_timeout)(us_socket_r s));
void us_socket_context_on_long_timeout(int ssl, us_socket_context_r context,
struct us_socket_t *(*on_timeout)(us_socket_r s));
/* This one is only used for when a connecting socket fails in a late stage. */
void us_socket_context_on_connect_error(int ssl, struct us_socket_context_t *context,
void us_socket_context_on_connect_error(int ssl, us_socket_context_r context,
struct us_connecting_socket_t *(*on_connect_error)(struct us_connecting_socket_t *s, int code));
void us_socket_context_on_socket_connect_error(int ssl, struct us_socket_context_t *context,
struct us_socket_t *(*on_connect_error)(struct us_socket_t *s, int code));
void us_socket_context_on_socket_connect_error(int ssl, us_socket_context_r context,
struct us_socket_t *(*on_connect_error)(us_socket_r s, int code));
void us_socket_context_on_handshake(int ssl, struct us_socket_context_t *context, void (*on_handshake)(struct us_socket_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data), void* custom_data);
void us_socket_context_on_handshake(int ssl, us_socket_context_r context, void (*on_handshake)(struct us_socket_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data), void* custom_data);
/* Emitted when a socket has been half-closed */
void us_socket_context_on_end(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_end)(struct us_socket_t *s));
void us_socket_context_on_end(int ssl, us_socket_context_r context, struct us_socket_t *(*on_end)(us_socket_r s));
/* Returns user data extension for this socket context */
void *us_socket_context_ext(int ssl, struct us_socket_context_t *context);
void *us_socket_context_ext(int ssl, us_socket_context_r context);
/* Closes all open sockets, including listen sockets. Does not invalidate the socket context. */
void us_socket_context_close(int ssl, struct us_socket_context_t *context);
void us_socket_context_close(int ssl, us_socket_context_r context);
/* Listen for connections. Acts as the main driving cog in a server. Will call set async callbacks. */
struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context,
struct us_listen_socket_t *us_socket_context_listen(int ssl, us_socket_context_r context,
const char *host, int port, int options, int socket_ext_size);
struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_socket_context_t *context,
struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, us_socket_context_r context,
const char *path, size_t pathlen, int options, int socket_ext_size);
/* listen_socket.c/.h */
void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls);
void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) nonnull_fn_decl;
/*
Returns one of
@@ -281,156 +304,156 @@ void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls);
This is the slow path where we must either go through DNS resolution or create multiple sockets
per the happy eyeballs algorithm
*/
void *us_socket_context_connect(int ssl, struct us_socket_context_t *context,
const char *host, int port, int options, int socket_ext_size, int *is_connecting);
void *us_socket_context_connect(int ssl, struct us_socket_context_t * nonnull_arg context,
const char *host, int port, int options, int socket_ext_size, int *is_connecting) __attribute__((nonnull(2)));
struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_context_t *context,
const char *server_path, size_t pathlen, int options, int socket_ext_size);
struct us_socket_t *us_socket_context_connect_unix(int ssl, us_socket_context_r context,
const char *server_path, size_t pathlen, int options, int socket_ext_size) __attribute__((nonnull(2)));
/* Is this socket established? Can be used to check if a connecting socket has fired the on_open event yet.
* Can also be used to determine if a socket is a listen_socket or not, but you probably know that already. */
int us_socket_is_established(int ssl, struct us_socket_t *s);
int us_socket_is_established(int ssl, us_socket_r s) nonnull_fn_decl;
void us_connecting_socket_free(struct us_connecting_socket_t *c);
void us_connecting_socket_free(struct us_connecting_socket_t *c) nonnull_fn_decl;
/* Cancel a connecting socket. Can be used together with us_socket_timeout to limit connection times.
* Entirely destroys the socket - this function works like us_socket_close but does not trigger on_close event since
* you never got the on_open event first. */
void us_connecting_socket_close(int ssl, struct us_connecting_socket_t *c);
void us_connecting_socket_close(int ssl, struct us_connecting_socket_t *c) nonnull_fn_decl;
/* Returns the loop for this socket context. */
struct us_loop_t *us_socket_context_loop(int ssl, struct us_socket_context_t *context);
struct us_loop_t *us_socket_context_loop(int ssl, us_socket_context_r context) nonnull_fn_decl __attribute((returns_nonnull));
/* Invalidates passed socket, returning a new resized socket which belongs to a different socket context.
* Used mainly for "socket upgrades" such as when transitioning from HTTP to WebSocket. */
struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s, int ext_size);
struct us_socket_t *us_socket_context_adopt_socket(int ssl, us_socket_context_r context, us_socket_r s, int ext_size);
/* Create a child socket context which acts much like its own socket context with its own callbacks yet still relies on the
* parent socket context for some shared resources. Child socket contexts should be used together with socket adoptions and nothing else. */
struct us_socket_context_t *us_create_child_socket_context(int ssl, struct us_socket_context_t *context, int context_ext_size);
struct us_socket_context_t *us_create_child_socket_context(int ssl, us_socket_context_r context, int context_ext_size);
/* Public interfaces for loops */
/* Returns a new event loop with user data extension */
struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t *loop),
void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop), unsigned int ext_size);
struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(us_loop_r loop),
void (*pre_cb)(us_loop_r loop), void (*post_cb)(us_loop_r loop), unsigned int ext_size);
/* Frees the loop immediately */
void us_loop_free(struct us_loop_t *loop);
void us_loop_free(us_loop_r loop) nonnull_fn_decl;
/* Returns the loop user data extension */
void *us_loop_ext(struct us_loop_t *loop);
void *us_loop_ext(us_loop_r loop) nonnull_fn_decl;
/* Blocks the calling thread and drives the event loop until no more non-fallthrough polls are scheduled */
void us_loop_run(struct us_loop_t *loop);
void us_loop_run(us_loop_r loop) nonnull_fn_decl;
/* Signals the loop from any thread to wake up and execute its wakeup handler from the loop's own running thread.
* This is the only fully thread-safe function and serves as the basis for thread safety */
void us_wakeup_loop(struct us_loop_t *loop);
void us_wakeup_loop(us_loop_r loop) nonnull_fn_decl;
/* Hook up timers in existing loop */
void us_loop_integrate(struct us_loop_t *loop);
void us_loop_integrate(us_loop_r loop) nonnull_fn_decl;
/* Returns the loop iteration number */
long long us_loop_iteration_number(struct us_loop_t *loop);
long long us_loop_iteration_number(us_loop_r loop) nonnull_fn_decl;
/* Public interfaces for polls */
/* A fallthrough poll does not keep the loop running, it falls through */
struct us_poll_t *us_create_poll(struct us_loop_t *loop, int fallthrough, unsigned int ext_size);
struct us_poll_t *us_create_poll(us_loop_r loop, int fallthrough, unsigned int ext_size);
/* After stopping a poll you must manually free the memory */
void us_poll_free(struct us_poll_t *p, struct us_loop_t *loop);
void us_poll_free(us_poll_r p, struct us_loop_t *loop);
/* Associate this poll with a socket descriptor and poll type */
void us_poll_init(struct us_poll_t *p, LIBUS_SOCKET_DESCRIPTOR fd, int poll_type);
void us_poll_init(us_poll_r p, LIBUS_SOCKET_DESCRIPTOR fd, int poll_type);
/* Start, change and stop polling for events */
void us_poll_start(struct us_poll_t *p, struct us_loop_t *loop, int events);
void us_poll_change(struct us_poll_t *p, struct us_loop_t *loop, int events);
void us_poll_stop(struct us_poll_t *p, struct us_loop_t *loop);
void us_poll_start(us_poll_r p, us_loop_r loop, int events) nonnull_fn_decl;
void us_poll_change(us_poll_r p, us_loop_r loop, int events) nonnull_fn_decl;
void us_poll_stop(us_poll_r p, struct us_loop_t *loop) nonnull_fn_decl;
/* Return what events we are polling for */
int us_poll_events(struct us_poll_t *p);
int us_poll_events(us_poll_r p) nonnull_fn_decl;
/* Returns the user data extension of this poll */
void *us_poll_ext(struct us_poll_t *p);
void *us_poll_ext(us_poll_r p) nonnull_fn_decl;
/* Get associated socket descriptor from a poll */
LIBUS_SOCKET_DESCRIPTOR us_poll_fd(struct us_poll_t *p);
LIBUS_SOCKET_DESCRIPTOR us_poll_fd(us_poll_r p) nonnull_fn_decl;
/* Resize an active poll */
struct us_poll_t *us_poll_resize(struct us_poll_t *p, struct us_loop_t *loop, unsigned int ext_size);
struct us_poll_t *us_poll_resize(us_poll_r p, us_loop_r loop, unsigned int ext_size) nonnull_fn_decl;
/* Public interfaces for sockets */
/* Returns the underlying native handle for a socket, such as SSL or file descriptor.
* In the case of file descriptor, the value of pointer is fd. */
void *us_socket_get_native_handle(int ssl, struct us_socket_t *s);
void *us_socket_get_native_handle(int ssl, us_socket_r s) nonnull_fn_decl;
/* Write up to length bytes of data. Returns actual bytes written.
* Will call the on_writable callback of active socket context on failure to write everything off in one go.
* Set hint msg_more if you have more immediate data to write. */
int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more);
int us_socket_write(int ssl, us_socket_r s, const char * nonnull_arg data, int length, int msg_more) nonnull_fn_decl;
/* Special path for non-SSL sockets. Used to send header and payload in one go. Works like us_socket_write. */
int us_socket_write2(int ssl, struct us_socket_t *s, const char *header, int header_length, const char *payload, int payload_length);
int us_socket_write2(int ssl, us_socket_r s, const char *header, int header_length, const char *payload, int payload_length) nonnull_fn_decl;
/* Set a low precision, high performance timer on a socket. A socket can only have one single active timer
* at any given point in time. Will remove any such pre set timer */
void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds);
void us_socket_timeout(int ssl, us_socket_r s, unsigned int seconds) nonnull_fn_decl;
/* Set a low precision, high performance timer on a socket. Suitable for per-minute precision. */
void us_socket_long_timeout(int ssl, struct us_socket_t *s, unsigned int minutes);
void us_socket_long_timeout(int ssl, us_socket_r s, unsigned int minutes) nonnull_fn_decl;
/* Return the user data extension of this socket */
void *us_socket_ext(int ssl, struct us_socket_t *s);
void *us_connecting_socket_ext(int ssl, struct us_connecting_socket_t *c);
void *us_socket_ext(int ssl, us_socket_r s) nonnull_fn_decl;
void *us_connecting_socket_ext(int ssl, struct us_connecting_socket_t *c) nonnull_fn_decl;
/* Return the socket context of this socket */
struct us_socket_context_t *us_socket_context(int ssl, struct us_socket_t *s);
struct us_socket_context_t *us_socket_context(int ssl, us_socket_r s) nonnull_fn_decl __attribute__((returns_nonnull));
/* Withdraw any msg_more status and flush any pending data */
void us_socket_flush(int ssl, struct us_socket_t *s);
void us_socket_flush(int ssl, us_socket_r s) nonnull_fn_decl;
/* Shuts down the connection by sending FIN and/or close_notify */
void us_socket_shutdown(int ssl, struct us_socket_t *s);
void us_socket_shutdown(int ssl, us_socket_r s) nonnull_fn_decl;
/* Shuts down the connection in terms of read, meaning next event loop
* iteration will catch the socket being closed. Can be used to defer closing
* to next event loop iteration. */
void us_socket_shutdown_read(int ssl, struct us_socket_t *s);
void us_socket_shutdown_read(int ssl, us_socket_r s) nonnull_fn_decl;
/* Returns whether the socket has been shut down or not */
int us_socket_is_shut_down(int ssl, struct us_socket_t *s);
int us_socket_is_shut_down(int ssl, us_socket_r s) nonnull_fn_decl;
/* Returns whether this socket has been closed. Only valid if memory has not yet been released. */
int us_socket_is_closed(int ssl, struct us_socket_t *s);
int us_socket_is_closed(int ssl, us_socket_r s) nonnull_fn_decl;
/* Immediately closes the socket */
struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, void *reason);
struct us_socket_t *us_socket_close(int ssl, us_socket_r s, int code, void *reason) __attribute__((nonnull(2)));
/* Returns local port or -1 on failure. */
int us_socket_local_port(int ssl, struct us_socket_t *s);
int us_socket_local_port(int ssl, us_socket_r s) nonnull_fn_decl;
/* Copy remote (IP) address of socket, or fail with zero length. */
void us_socket_remote_address(int ssl, struct us_socket_t *s, char *buf, int *length);
void us_socket_local_address(int ssl, struct us_socket_t *s, char *buf, int *length);
void us_socket_remote_address(int ssl, us_socket_r s, char *nonnull_arg buf, int *nonnull_arg length) nonnull_fn_decl;
void us_socket_local_address(int ssl, us_socket_r s, char *nonnull_arg buf, int *nonnull_arg length) nonnull_fn_decl;
/* Bun extras */
struct us_socket_t *us_socket_pair(struct us_socket_context_t *ctx, int socket_ext_size, LIBUS_SOCKET_DESCRIPTOR* fds);
struct us_socket_t *us_socket_from_fd(struct us_socket_context_t *ctx, int socket_ext_size, LIBUS_SOCKET_DESCRIPTOR fd);
struct us_socket_t *us_socket_attach(int ssl, LIBUS_SOCKET_DESCRIPTOR client_fd, struct us_socket_context_t *ctx, int flags, int socket_ext_size);
struct us_socket_t *us_socket_wrap_with_tls(int ssl, struct us_socket_t *s, struct us_bun_socket_context_options_t options, struct us_socket_events_t events, int socket_ext_size);
int us_socket_raw_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more);
struct us_socket_t *us_socket_wrap_with_tls(int ssl, us_socket_r s, struct us_bun_socket_context_options_t options, struct us_socket_events_t events, int socket_ext_size);
int us_socket_raw_write(int ssl, us_socket_r s, const char *data, int length, int msg_more);
struct us_socket_t* us_socket_open(int ssl, struct us_socket_t * s, int is_client, char* ip, int ip_length);
int us_raw_root_certs(struct us_cert_string_t**out);
unsigned int us_get_remote_address_info(char *buf, struct us_socket_t *s, const char **dest, int *port, int *is_ipv6);
int us_socket_get_error(int ssl, struct us_socket_t *s);
unsigned int us_get_remote_address_info(char *buf, us_socket_r s, const char **dest, int *port, int *is_ipv6);
int us_socket_get_error(int ssl, us_socket_r s);
void us_socket_ref(struct us_socket_t *s);
void us_socket_unref(struct us_socket_t *s);
void us_socket_ref(us_socket_r s);
void us_socket_unref(us_socket_r s);
#ifdef __cplusplus
}

View File

@@ -47,6 +47,8 @@ void us_internal_loop_data_init(struct us_loop_t *loop, void (*wakeup_cb)(struct
loop->data.parent_ptr = 0;
loop->data.parent_tag = 0;
loop->data.closed_context_head = 0;
loop->data.wakeup_async = us_internal_create_async(loop, 1, 0);
us_internal_async_set(loop->data.wakeup_async, (void (*)(struct us_internal_async *)) wakeup_cb);
}
@@ -234,6 +236,15 @@ void us_internal_free_closed_sockets(struct us_loop_t *loop) {
loop->data.closed_connecting_head = 0;
}
void us_internal_free_closed_contexts(struct us_loop_t *loop) {
for (struct us_socket_context_t *ctx = loop->data.closed_context_head; ctx; ) {
struct us_socket_context_t *next = ctx->next;
us_free(ctx);
ctx = next;
}
loop->data.closed_context_head = 0;
}
void sweep_timer_cb(struct us_internal_callback_t *cb) {
us_internal_timer_sweep(cb->loop);
}
@@ -253,6 +264,7 @@ void us_internal_loop_pre(struct us_loop_t *loop) {
void us_internal_loop_post(struct us_loop_t *loop) {
us_internal_handle_dns_results(loop);
us_internal_free_closed_sockets(loop);
us_internal_free_closed_contexts(loop);
loop->data.post_cb(loop);
}
@@ -356,7 +368,8 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events)
s->context->loop->data.low_prio_budget--; /* Still having budget for this iteration - do normal processing */
} else {
us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_WRITABLE);
us_internal_socket_context_unlink_socket(s->context, s);
us_socket_context_ref(0, s->context);
us_internal_socket_context_unlink_socket(0, s->context, s);
/* Link this socket to the low-priority queue - we use a LIFO queue, to prioritize newer clients that are
* maybe not already timeouted - sounds unfair, but works better in real-life with smaller client-timeouts
@@ -411,7 +424,8 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events)
if (us_socket_is_shut_down(0, s)) {
/* We got FIN back after sending it */
/* Todo: We should give "CLEAN SHUTDOWN" as reason here */
s = us_socket_close(0, s, 0, NULL);
s = us_socket_close(0, s, LIBUS_SOCKET_CLOSE_CODE_CLEAN_SHUTDOWN, NULL);
return;
} else {
/* We got FIN, so stop polling for readable */
us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_WRITABLE);
@@ -419,7 +433,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events)
}
} else if (length == LIBUS_SOCKET_ERROR && !bsd_would_block()) {
/* Todo: decide also here what kind of reason we should give */
s = us_socket_close(0, s, 0, NULL);
s = us_socket_close(0, s, LIBUS_SOCKET_CLOSE_CODE_CLEAN_SHUTDOWN, NULL);
return;
}

View File

@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#ifndef WIN32
#include <fcntl.h>
@@ -113,6 +114,9 @@ void us_socket_flush(int ssl, struct us_socket_t *s) {
}
int us_socket_is_closed(int ssl, struct us_socket_t *s) {
if(ssl) {
return us_internal_ssl_socket_is_closed((struct us_internal_ssl_socket_t *) s);
}
return s->prev == (struct us_socket_t *) s->context;
}
@@ -137,7 +141,7 @@ void us_connecting_socket_close(int ssl, struct us_connecting_socket_t *c) {
c->closed = 1;
for (struct us_socket_t *s = c->connecting_head; s; s = s->connect_next) {
us_internal_socket_context_unlink_socket(s->context, s);
us_internal_socket_context_unlink_socket(ssl, s->context, s);
us_poll_stop((struct us_poll_t *) s, s->context->loop);
bsd_close_socket(us_poll_fd((struct us_poll_t *) s));
@@ -157,6 +161,9 @@ void us_connecting_socket_close(int ssl, struct us_connecting_socket_t *c) {
}
struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, void *reason) {
if(ssl) {
return (struct us_socket_t *)us_internal_ssl_socket_close((struct us_internal_ssl_socket_t *) s, code, reason);
}
if (!us_socket_is_closed(0, s)) {
if (s->low_prio_state == 1) {
/* Unlink this socket from the low-priority queue */
@@ -168,8 +175,10 @@ struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, vo
s->prev = 0;
s->next = 0;
s->low_prio_state = 0;
us_socket_context_unref(ssl, s->context);
} else {
us_internal_socket_context_unlink_socket(s->context, s);
us_internal_socket_context_unlink_socket(ssl, s->context, s);
}
#ifdef LIBUS_USE_KQUEUE
// kqueue automatically removes the fd from the set on close
@@ -218,8 +227,10 @@ struct us_socket_t *us_socket_detach(int ssl, struct us_socket_t *s) {
s->prev = 0;
s->next = 0;
s->low_prio_state = 0;
us_socket_context_unref(ssl, s->context);
} else {
us_internal_socket_context_unlink_socket(s->context, s);
us_internal_socket_context_unlink_socket(ssl, s->context, s);
}
us_poll_stop((struct us_poll_t *) s, s->context->loop);

View File

@@ -1,38 +0,0 @@
CAPI_EXAMPLE_FILES := HelloWorld HelloWorldAsync ServerName UpgradeSync UpgradeAsync EchoServer Broadcast BroadcastEchoServer
RUST_EXAMPLE_FILES := RustHelloWorld
LIBRARY_NAME := libuwebsockets
default:
$(MAKE) capi
$(CXX) -O3 -flto -I ../src -I ../uSockets/src examples/HelloWorld.c *.o -lz -luv -lssl -lcrypto -lstdc++ ../uSockets/uSockets.a -o HelloWorld
capi:
$(MAKE) clean
cd ../uSockets && $(CC) -pthread -DUWS_WITH_PROXY -DLIBUS_USE_OPENSSL -DLIBUS_USE_LIBUV -std=c11 -Isrc -flto -fPIC -O3 -c src/*.c src/eventing/*.c src/crypto/*.c
cd ../uSockets && $(CXX) -std=c++17 -flto -fPIC -O3 -c src/crypto/*.cpp
cd ../uSockets && $(AR) rvs uSockets.a *.o
$(CXX) -DUWS_WITH_PROXY -c -O3 -std=c++17 -lz -luv -flto -fPIC -I ../src -I ../uSockets/src $(LIBRARY_NAME).cpp
$(AR) rvs $(LIBRARY_NAME).a $(LIBRARY_NAME).o ../uSockets/uSockets.a
shared:
$(MAKE) clean
cd ../uSockets && $(CC) -pthread -DUWS_WITH_PROXY -DLIBUS_USE_OPENSSL -DLIBUS_USE_LIBUV -std=c11 -Isrc -flto -fPIC -O3 -c src/*.c src/eventing/*.c src/crypto/*.c
cd ../uSockets && $(CXX) -std=c++17 -flto -fPIC -O3 -c src/crypto/*.cpp
cd ../uSockets && $(AR) rvs uSockets.a *.o
$(CXX) -DUWS_WITH_PROXY -c -O3 -std=c++17 -lz -luv -flto -fPIC -I ../src -I ../uSockets/src $(LIBRARY_NAME).cpp
$(CXX) -shared -o $(LIBRARY_NAME).so $(LIBRARY_NAME).o ../uSockets/uSockets.a -fPIC -lz -luv -lssl -lcrypto
misc:
mkdir -p ../misc && openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -passout pass:1234 -keyout ../misc/key.pem -out ../misc/cert.pem
rust:
$(MAKE) capi
rustc -C link-arg=$(LIBRARY_NAME).a -C link-args="-lstdc++ -luv" -C opt-level=3 -C lto -L all=. examples/RustHelloWorld.rs -o RustHelloWorld
clean:
rm -f *.o $(CAPI_EXAMPLE_FILES) $(RUST_EXAMPLE_FILES) $(LIBRARY_NAME).a $(LIBRARY_NAME).so
all:
for FILE in $(CAPI_EXAMPLE_FILES); do $(CXX) -O3 -flto -I ../src -I ../uSockets/src examples/$$FILE.c *.o -luv -lstdc++ ../uSockets/uSockets.a -o $$FILE & done; \
wait

View File

@@ -1,157 +0,0 @@
#include "../libuwebsockets.h"
#include <stdio.h>
#include <malloc.h>
#include <time.h>
#include <string.h>
#include <stdarg.h>
#define SSL 1
//Timer close helper
void uws_timer_close(struct us_timer_t *timer)
{
struct us_timer_t *t = (struct us_timer_t *)timer;
struct timer_handler_data *data;
memcpy(&data, us_timer_ext(t), sizeof(struct timer_handler_data *));
free(data);
us_timer_close(t, 0);
}
//Timer create helper
struct us_timer_t *uws_create_timer(int ms, int repeat_ms, void (*handler)(void *data), void *data)
{
struct us_loop_t *loop = uws_get_loop();
struct us_timer_t *delayTimer = us_create_timer(loop, 0, sizeof(void *));
struct timer_handler_data
{
void *data;
void (*handler)(void *data);
bool repeat;
};
struct timer_handler_data *timer_data = (struct timer_handler_data *)malloc(sizeof(timer_handler_data));
timer_data->data = data;
timer_data->handler = handler;
timer_data->repeat = repeat_ms > 0;
memcpy(us_timer_ext(delayTimer), &timer_data, sizeof(struct timer_handler_data *));
us_timer_set(
delayTimer, [](struct us_timer_t *t)
{
/* We wrote the pointer to the timer's extension */
struct timer_handler_data *data;
memcpy(&data, us_timer_ext(t), sizeof(struct timer_handler_data *));
data->handler(data->data);
if (!data->repeat)
{
free(data);
us_timer_close(t, 0);
}
},
ms, repeat_ms);
return (struct us_timer_t *)delayTimer;
}
/* This is a simple WebSocket "sync" upgrade example.
* You may compile it with "WITH_OPENSSL=1 make" or with "make" */
/* ws->getUserData returns one of these */
struct PerSocketData {
/* Fill with user data */
};
int buffer_size(const char* format, ...) {
va_list args;
va_start(args, format);
int result = vsnprintf(NULL, 0, format, args);
va_end(args);
return result + 1; // safe byte for \0
}
void listen_handler(struct us_listen_socket_t *listen_socket, uws_app_listen_config_t config, void* user_data)
{
if (listen_socket){
printf("Listening on port wss://localhost:%d\n", config.port);
}
}
void open_handler(uws_websocket_t* ws){
/* Open event here, you may access uws_ws_get_user_data(WS) which points to a PerSocketData struct */
uws_ws_subscribe(SSL, ws, "broadcast", 9);
}
void message_handler(uws_websocket_t* ws, const char* message, size_t length, uws_opcode_t opcode){
}
void close_handler(uws_websocket_t* ws, int code, const char* message, size_t length){
/* You may access uws_ws_get_user_data(ws) here, but sending or
* doing any kind of I/O with the socket is not valid. */
}
void drain_handler(uws_websocket_t* ws){
/* Check uws_ws_get_buffered_amount(ws) here */
}
void ping_handler(uws_websocket_t* ws, const char* message, size_t length){
/* You don't need to handle this one, we automatically respond to pings as per standard */
}
void pong_handler(uws_websocket_t* ws, const char* message, size_t length){
/* You don't need to handle this one either */
}
void on_timer_interval(void* data){
// broadcast the unix time as millis
uws_app_t * app = (uws_app_t *)data;
struct timespec ts;
timespec_get(&ts, TIME_UTC);
int64_t millis = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
char* message = (char*)malloc((size_t)buffer_size("%ld", millis));
size_t message_length = sprintf(message, "%ld", millis);
uws_publish(SSL, app, "broadcast", 9, message, message_length, uws_opcode_t::TEXT, false);
free(message);
}
int main()
{
uws_app_t *app = uws_create_app(SSL, (struct us_socket_context_options_t){
/* There are example certificates in uWebSockets.js repo */
.key_file_name = "../misc/key.pem",
.cert_file_name = "../misc/cert.pem",
.passphrase = "1234"
});
uws_ws(SSL, app, "/*", (uws_socket_behavior_t){
.compression = uws_compress_options_t::SHARED_COMPRESSOR,
.maxPayloadLength = 16 * 1024,
.idleTimeout = 12,
.maxBackpressure = 1 * 1024 * 1024,
.upgrade = NULL,
.open = open_handler,
.message = message_handler,
.drain = drain_handler,
.ping = ping_handler,
.pong = pong_handler,
.close = close_handler,
});
uws_app_listen(SSL, app, 9001, listen_handler, NULL);
// broadcast the unix time as millis every 8 millis
uws_create_timer(8, 8, on_timer_interval, app);
uws_app_run(SSL, app);
}

View File

@@ -1,175 +0,0 @@
#include "../libuwebsockets.h"
#include <stdio.h>
#include <malloc.h>
#include <time.h>
#include <string.h>
#include <stdarg.h>
#define SSL 1
/* This is a simple WebSocket "sync" upgrade example.
* You may compile it with "WITH_OPENSSL=1 make" or with "make" */
typedef struct
{
size_t length;
char *name;
} topic_t;
/* ws->getUserData returns one of these */
struct PerSocketData
{
/* Fill with user data */
topic_t **topics;
int topics_quantity;
int nr;
};
uws_app_t *app;
int buffer_size(const char *format, ...)
{
va_list args;
va_start(args, format);
int result = vsnprintf(NULL, 0, format, args);
va_end(args);
return result + 1; // safe byte for \0
}
void listen_handler(struct us_listen_socket_t *listen_socket, uws_app_listen_config_t config, void* user_data)
{
if (listen_socket)
{
printf("Listening on port wss://localhost:%d\n", config.port);
}
}
void upgrade_handler(uws_res_t *response, uws_req_t *request, uws_socket_context_t *context)
{
/* You may read from req only here, and COPY whatever you need into your PerSocketData.
* PerSocketData is valid from .open to .close event, accessed with uws_ws_get_user_data(ws).
* HttpRequest (req) is ONLY valid in this very callback, so any data you will need later
* has to be COPIED into PerSocketData here. */
/* Immediately upgrading without doing anything "async" before, is simple */
struct PerSocketData *data = (struct PerSocketData *)malloc(sizeof(struct PerSocketData));
data->topics = (topic_t **)calloc(32, sizeof(topic_t *));
data->topics_quantity = 32;
data->nr = 0;
const char *ws_key = NULL;
const char *ws_protocol = NULL;
const char *ws_extensions = NULL;
size_t ws_key_length = uws_req_get_header(request, "sec-websocket-key", 17, &ws_key);
size_t ws_protocol_length = uws_req_get_header(request, "sec-websocket-protocol", 22, &ws_protocol);
size_t ws_extensions_length = uws_req_get_header(request, "sec-websocket-extensions", 24, &ws_extensions);
uws_res_upgrade(SSL,
response,
(void *)data,
ws_key,
ws_key_length,
ws_protocol,
ws_protocol_length,
ws_extensions,
ws_extensions_length,
context);
}
void open_handler(uws_websocket_t *ws)
{
/* Open event here, you may access uws_ws_get_user_data(ws) which points to a PerSocketData struct */
struct PerSocketData *data = (struct PerSocketData *)uws_ws_get_user_data(SSL, ws);
for (int i = 0; i < data->topics_quantity; i++)
{
char *topic = (char *)malloc((size_t)buffer_size("%ld-%d", (uintptr_t)ws, i));
size_t topic_length = sprintf(topic, "%ld-%d", (uintptr_t)ws, i);
topic_t *new_topic = (topic_t*) malloc(sizeof(topic_t));
new_topic->length = topic_length;
new_topic->name = topic;
data->topics[i] = new_topic;
uws_ws_subscribe(SSL, ws, topic, topic_length);
}
}
void message_handler(uws_websocket_t *ws, const char *message, size_t length, uws_opcode_t opcode)
{
struct PerSocketData *data = (struct PerSocketData *)uws_ws_get_user_data(SSL, ws);
topic_t *topic = data->topics[(size_t)(++data->nr % data->topics_quantity)];
uws_publish(SSL, app, topic->name, topic->length, message, length, opcode, false);
topic = data->topics[(size_t)(++data->nr % data->topics_quantity)];
uws_ws_publish(SSL, ws, topic->name, topic->length, message, length);
}
void close_handler(uws_websocket_t *ws, int code, const char *message, size_t length)
{
/* You may access uws_ws_get_user_data(ws) here, but sending or
* doing any kind of I/O with the socket is not valid. */
struct PerSocketData *data = (struct PerSocketData *)uws_ws_get_user_data(SSL, ws);
if (data)
{
for (int i = 0; i < data->topics_quantity; i++)
{
topic_t* topic = data->topics[i];
free(topic->name);
free(topic);
}
free(data->topics);
free(data);
}
}
void drain_handler(uws_websocket_t *ws)
{
/* Check uws_ws_get_buffered_amount(ws) here */
}
void ping_handler(uws_websocket_t *ws, const char *message, size_t length)
{
/* You don't need to handle this one, we automatically respond to pings as per standard */
}
void pong_handler(uws_websocket_t *ws, const char *message, size_t length)
{
/* You don't need to handle this one either */
}
int main()
{
uws_app_t *app = uws_create_app(SSL, (struct us_socket_context_options_t){
/* There are example certificates in uWebSockets.js repo */
.key_file_name = "../misc/key.pem",
.cert_file_name = "../misc/cert.pem",
.passphrase = "1234"
});
uws_ws(SSL, app, "/*", (uws_socket_behavior_t){
.compression = uws_compress_options_t::SHARED_COMPRESSOR,
.maxPayloadLength = 16 * 1024,
.idleTimeout = 12,
.maxBackpressure = 1 * 1024 * 1024,
.upgrade = upgrade_handler,
.open = open_handler,
.message = message_handler,
.drain = drain_handler,
.ping = ping_handler,
.pong = pong_handler,
.close = close_handler,
});
uws_app_listen(SSL, app, 9001, listen_handler, NULL);
uws_app_run(SSL, app);
}

View File

@@ -1,81 +0,0 @@
#include "../libuwebsockets.h"
#include <stdio.h>
#include <malloc.h>
#define SSL 1
/* This is a simple WebSocket "sync" upgrade example.
* You may compile it with "WITH_OPENSSL=1 make" or with "make" */
/* ws->getUserData returns one of these */
struct PerSocketData {
/* Fill with user data */
};
void listen_handler(struct us_listen_socket_t *listen_socket, uws_app_listen_config_t config, void* user_data)
{
if (listen_socket){
printf("Listening on port wss://localhost:%d\n", config.port);
}
}
void open_handler(uws_websocket_t* ws){
/* Open event here, you may access uws_ws_get_user_data(WS) which points to a PerSocketData struct */
}
void message_handler(uws_websocket_t* ws, const char* message, size_t length, uws_opcode_t opcode){
uws_ws_send(SSL, ws, message, length, opcode);
}
void close_handler(uws_websocket_t* ws, int code, const char* message, size_t length){
/* You may access uws_ws_get_user_data(ws) here, but sending or
* doing any kind of I/O with the socket is not valid. */
}
void drain_handler(uws_websocket_t* ws){
/* Check uws_ws_get_buffered_amount(ws) here */
}
void ping_handler(uws_websocket_t* ws, const char* message, size_t length){
/* You don't need to handle this one, we automatically respond to pings as per standard */
}
void pong_handler(uws_websocket_t* ws, const char* message, size_t length){
/* You don't need to handle this one either */
}
int main()
{
uws_app_t *app = uws_create_app(SSL, (struct us_socket_context_options_t){
/* There are example certificates in uWebSockets.js repo */
.key_file_name = "../misc/key.pem",
.cert_file_name = "../misc/cert.pem",
.passphrase = "1234"
});
uws_ws(SSL, app, "/*", (uws_socket_behavior_t){
.compression = uws_compress_options_t::SHARED_COMPRESSOR,
.maxPayloadLength = 16 * 1024,
.idleTimeout = 12,
.maxBackpressure = 1 * 1024 * 1024,
.upgrade = NULL,
.open = open_handler,
.message = message_handler,
.drain = drain_handler,
.ping = ping_handler,
.pong = pong_handler,
.close = close_handler,
});
uws_app_listen(SSL,app, 9001, listen_handler, NULL);
uws_app_run(SSL, app);
}

View File

@@ -1,33 +0,0 @@
#include "../libuwebsockets.h"
#include "libusockets.h"
#include <stdio.h>
#define SSL 1
void get_handler(uws_res_t *res, uws_req_t *req, void *user_data)
{
uws_res_end(SSL, res, "Hello CAPI!", 11, false);
}
void listen_handler(struct us_listen_socket_t *listen_socket, uws_app_listen_config_t config, void *user_data)
{
if (listen_socket)
{
printf("Listening on port https://localhost:%d now\n", config.port);
}
}
int main()
{
/* Overly simple hello world app */
uws_app_t *app = uws_create_app(SSL, (struct us_socket_context_options_t){
/* There are example certificates in uWebSockets.js repo */
.key_file_name = "../misc/key.pem",
.cert_file_name = "../misc/cert.pem",
.passphrase = "1234"
});
uws_app_get(SSL, app, "/*", get_handler, NULL);
uws_app_listen(SSL, app, 3000, listen_handler, NULL);
uws_app_run(SSL, app);
}

View File

@@ -1,123 +0,0 @@
#include "../libuwebsockets.h"
#include "libusockets.h"
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#define SSL 0
typedef struct {
uws_res_t* res;
bool aborted;
} async_request_t;
//Timer close helper
void uws_timer_close(struct us_timer_t *timer)
{
struct us_timer_t *t = (struct us_timer_t *)timer;
struct timer_handler_data *data;
memcpy(&data, us_timer_ext(t), sizeof(struct timer_handler_data *));
free(data);
us_timer_close(t, 0);
}
//Timer create helper
struct us_timer_t *uws_create_timer(int ms, int repeat_ms, void (*handler)(void *data), void *data)
{
struct us_loop_t *loop = uws_get_loop();
struct us_timer_t *delayTimer = us_create_timer(loop, 0, sizeof(void *));
struct timer_handler_data
{
void *data;
void (*handler)(void *data);
bool repeat;
};
struct timer_handler_data *timer_data = (struct timer_handler_data *)malloc(sizeof(timer_handler_data));
timer_data->data = data;
timer_data->handler = handler;
timer_data->repeat = repeat_ms > 0;
memcpy(us_timer_ext(delayTimer), &timer_data, sizeof(struct timer_handler_data *));
us_timer_set(
delayTimer, [](struct us_timer_t *t)
{
/* We wrote the pointer to the timer's extension */
struct timer_handler_data *data;
memcpy(&data, us_timer_ext(t), sizeof(struct timer_handler_data *));
data->handler(data->data);
if (!data->repeat)
{
free(data);
us_timer_close(t, 0);
}
},
ms, repeat_ms);
return (struct us_timer_t *)delayTimer;
}
void on_res_aborted(uws_res_t *response, void* data){
async_request_t* request_data = (async_request_t*)data;
/* We don't implement any kind of cancellation here,
* so simply flag us as aborted */
request_data->aborted = true;
}
void on_res_corked(uws_res_t *response, void* data){
uws_res_end(SSL, response, "Hello CAPI!", 11, false);
}
void on_timer_done(void *data){
async_request_t* request_data = (async_request_t*)data;
/* Were'nt we aborted before our async task finished? Okay, send a message! */
if(!request_data->aborted){
uws_res_cork(SSL, request_data->res,on_res_corked, request_data);
}
}
void get_handler(uws_res_t *res, uws_req_t *req, void* user_data)
{
/* We have to attach an abort handler for us to be aware
* of disconnections while we perform async tasks */
async_request_t* request_data = (async_request_t*) malloc(sizeof(async_request_t));
request_data->res = res;
request_data->aborted = false;
uws_res_on_aborted(SSL, res, on_res_aborted, request_data);
/* Simulate checking auth for 5 seconds. This looks like crap, never write
* code that utilize us_timer_t like this; they are high-cost and should
* not be created and destroyed more than rarely!
* Either way, here we go!*/
uws_create_timer(1, 0, on_timer_done, request_data);
}
void listen_handler(struct us_listen_socket_t *listen_socket, uws_app_listen_config_t config, void* user_data)
{
if (listen_socket)
{
printf("Listening on port https://localhost:%d now\n", config.port);
}
}
int main()
{
/* Overly simple hello world app with async response */
uws_app_t *app = uws_create_app(SSL, (struct us_socket_context_options_t){
/* There are example certificates in uWebSockets.js repo */
.key_file_name = "../misc/key.pem",
.cert_file_name = "../misc/cert.pem",
.passphrase = "1234"
});
uws_app_get(SSL, app, "/*", get_handler, NULL);
uws_app_listen(SSL, app, 3000, listen_handler, NULL);
uws_app_run(SSL, app);
}

View File

@@ -1,309 +0,0 @@
/* automatically generated by rust-bindgen 0.59.2 */
use std::convert::TryInto;
use std::ffi::CString;
pub type SizeT = ::std::os::raw::c_ulong;
pub type WcharT = ::std::os::raw::c_uint;
#[repr(C)]
#[repr(align(16))]
#[derive(Debug, Copy, Clone)]
pub struct max_align_t {
pub __clang_max_align_nonce1: ::std::os::raw::c_longlong,
pub __bindgen_padding_0: u64,
pub __clang_max_align_nonce2: u128,
}
#[test]
fn bindgen_test_layout_max_align_t() {
assert_eq!(
::std::mem::size_of::<max_align_t>(),
32usize,
concat!("Size of: ", stringify!(max_align_t))
);
assert_eq!(
::std::mem::align_of::<max_align_t>(),
16usize,
concat!("Alignment of ", stringify!(max_align_t))
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<max_align_t>())).__clang_max_align_nonce1 as *const _ as usize
},
0usize,
concat!(
"Offset of field: ",
stringify!(max_align_t),
"::",
stringify!(__clang_max_align_nonce1)
)
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<max_align_t>())).__clang_max_align_nonce2 as *const _ as usize
},
16usize,
concat!(
"Offset of field: ",
stringify!(max_align_t),
"::",
stringify!(__clang_max_align_nonce2)
)
);
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct uws_app_s {
_unused: [u8; 0],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct uws_req_s {
_unused: [u8; 0],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct uws_res_s {
_unused: [u8; 0],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct uws_app_listen_config_s {
port: ::std::os::raw::c_int,
host: *const ::std::os::raw::c_char,
options: ::std::os::raw::c_int,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct us_socket_context_options_s {
key_file_name: *const ::std::os::raw::c_char,
cert_file_name: *const ::std::os::raw::c_char,
passphrase: *const ::std::os::raw::c_char,
dh_params_file_name: *const ::std::os::raw::c_char,
ca_file_name: *const ::std::os::raw::c_char,
ssl_prefer_low_memory_usage: ::std::os::raw::c_int,
}
pub type UwsAppListenConfigT = uws_app_listen_config_s;
pub type UsSocketContextOptionsT = us_socket_context_options_s;
pub struct UsSocketContextOptions<'a> {
key_file_name: &'a str,
cert_file_name: &'a str,
passphrase: &'a str,
dh_params_file_name: &'a str,
ca_file_name: &'a str,
ssl_prefer_low_memory_usage: i32,
}
pub type UwsAppT = uws_app_s;
pub type UwsReqT = uws_req_s;
pub type UwsResT = uws_res_s;
extern "C" {
pub fn uws_create_app(
ssl: ::std::os::raw::c_int,
options: UsSocketContextOptionsT,
) -> *mut UwsAppT;
pub fn uws_app_get(
ssl: ::std::os::raw::c_int,
app: *mut UwsAppT,
pattern: *const ::std::os::raw::c_char,
handler: ::std::option::Option<
unsafe extern "C" fn(
res: *mut UwsResT,
req: *mut UwsReqT,
user_data: *mut ::std::os::raw::c_void,
),
>,
user_data: *mut ::std::os::raw::c_void,
);
pub fn uws_app_run(ssl: ::std::os::raw::c_int, app: *mut UwsAppT);
pub fn uws_app_listen(
ssl: ::std::os::raw::c_int,
app: *mut UwsAppT,
port: ::std::os::raw::c_int,
handler: ::std::option::Option<
unsafe extern "C" fn(
listen_socket: *mut ::std::os::raw::c_void,
config: UwsAppListenConfigT,
user_data: *mut ::std::os::raw::c_void,
),
>,
user_data: *mut ::std::os::raw::c_void,
);
pub fn uws_res_end(
ssl: ::std::os::raw::c_int,
res: *mut UwsResT,
data: *const ::std::os::raw::c_char,
length: SizeT,
close_connection: bool,
);
}
pub struct AppResponse<const SSL: i32> {
native: *mut UwsResT,
}
pub struct AppRequest {
native: *mut UwsReqT,
}
impl AppRequest {
pub fn new(native: *mut UwsReqT) -> AppRequest {
AppRequest { native: native }
}
}
impl<const SSL: i32> AppResponse<SSL> {
pub fn new(native: *mut UwsResT) -> AppResponse<SSL> {
AppResponse::<SSL> { native: native }
}
fn end(self, message: &str) -> AppResponse<SSL> {
unsafe {
let c_message =
::std::ffi::CString::new(message).expect("Failed to create message CString");
//This will now const fold :/ performance impact needs refactor
uws_res_end(
SSL,
self.native,
c_message.as_ptr(),
message.len().try_into().unwrap(),
false,
);
}
self
}
}
pub type UwsMethodHandler<const SSL: i32> = fn(res: AppResponse<SSL>, req: AppRequest);
pub type UwsListenHandler =
fn(listen_socket: *mut ::std::os::raw::c_void, config: UwsAppListenConfigT);
pub struct TemplateApp<const SSL: i32> {
native: *mut UwsAppT,
}
extern "C" fn uws_generic_listen_handler(
listen_socket: *mut ::std::os::raw::c_void,
config: UwsAppListenConfigT,
user_data: *mut ::std::os::raw::c_void,
) {
unsafe {
let callback = &mut *(user_data as *mut UwsListenHandler);
callback(listen_socket, config);
}
}
extern "C" fn uws_generic_method_handler(
res: *mut UwsResT,
req: *mut UwsReqT,
user_data: *mut ::std::os::raw::c_void,
) {
unsafe {
let response = AppResponse::<0>::new(res);
let request = AppRequest::new(req);
let callback = &mut *(user_data as *mut UwsMethodHandler<0>);
callback(response, request);
}
}
extern "C" fn uws_ssl_generic_method_handler(
res: *mut UwsResT,
req: *mut UwsReqT,
user_data: *mut ::std::os::raw::c_void,
) {
unsafe {
let response = AppResponse::<1>::new(res);
let request = AppRequest::new(req);
let callback = &mut *(user_data as *mut UwsMethodHandler<1>);
callback(response, request);
}
}
impl<const SSL: i32> TemplateApp<SSL> {
pub fn new(config: UsSocketContextOptions) -> TemplateApp<SSL> {
unsafe {
let key_file_name_s =
CString::new(config.key_file_name).expect("Failed to create key_file_name CString");
let cert_file_name_s = CString::new(config.cert_file_name)
.expect("Failed to create cert_file_name CString");
let passphrase_s =
CString::new(config.passphrase).expect("Failed to create passphrase CString");
let dh_params_file_name_s = CString::new(config.dh_params_file_name)
.expect("Failed to create dh_params_file_name CString");
let ca_file_name_s =
CString::new(config.ca_file_name).expect("Failed to create ca_file_name CString");
let native_options = UsSocketContextOptionsT {
key_file_name: key_file_name_s.as_ptr(),
cert_file_name: cert_file_name_s.as_ptr(),
passphrase: passphrase_s.as_ptr(),
dh_params_file_name: dh_params_file_name_s.as_ptr(),
ca_file_name: ca_file_name_s.as_ptr(),
ssl_prefer_low_memory_usage: config.ssl_prefer_low_memory_usage,
};
TemplateApp::<SSL> {
native: uws_create_app(SSL, native_options),
}
}
}
pub fn get(self, route: &str, mut handler: UwsMethodHandler<SSL>) -> TemplateApp<SSL> {
unsafe {
let c_route = ::std::ffi::CString::new(route).expect("Failed to create route CString");
if SSL == 1 {
uws_app_get(
SSL,
self.native,
c_route.as_ptr(),
std::option::Option::Some(uws_ssl_generic_method_handler),
&mut handler as *mut _ as *mut ::std::os::raw::c_void,
);
} else {
uws_app_get(
SSL,
self.native,
c_route.as_ptr(),
std::option::Option::Some(uws_generic_method_handler),
&mut handler as *mut _ as *mut ::std::os::raw::c_void,
);
}
}
self
}
pub fn listen(self, port: i32, mut handler: UwsListenHandler) -> TemplateApp<SSL> {
unsafe {
uws_app_listen(
SSL,
self.native,
port,
::std::option::Option::Some(uws_generic_listen_handler),
&mut handler as *mut _ as *mut ::std::os::raw::c_void,
);
}
self
}
pub fn run(self) -> TemplateApp<SSL> {
unsafe {
uws_app_run(SSL, self.native);
}
self
}
}
pub type App = TemplateApp<0>;
pub type SSLApp = TemplateApp<1>;
fn main() {
let config = UsSocketContextOptions {
key_file_name: "../misc/key.pem",
cert_file_name: "../misc/cert.pem",
passphrase: "1234",
ca_file_name: "",
dh_params_file_name: "",
ssl_prefer_low_memory_usage: 0,
};
SSLApp::new(config)
.get("/", |res, _req| {
res.end("Hello Rust!");
})
.listen(3000, |_listen_socket, config| {
println!("Listening on port https://127.0.0.1:{}", config.port);
})
.run();
}

View File

@@ -1,59 +0,0 @@
#include "../libuwebsockets.h"
#include <stdio.h>
#include <string.h>
#define SSL 1
struct us_listen_socket_t *globalListenSocket;
uws_app_t *app;
void get_handler(uws_res_t *res, uws_req_t *req, void* user_data)
{
uws_res_end(SSL, res, "Hello CAPI!", 11, false);
}
void exit_handler(uws_res_t *res, uws_req_t *req, void* user_data)
{
uws_res_end(SSL, res, "Shutting down!",14, false);
/* We use this to check graceful closedown */
us_listen_socket_close(false, globalListenSocket);
}
void missing_server_name_handler(const char *hostname, void* user_data){
printf("We are missing server name: <%s>\n", hostname);
/* Assume it is localhost, so add it */
uws_add_server_name(SSL, app, "localhost");
}
void listen_handler(struct us_listen_socket_t *listen_socket, uws_app_listen_config_t config, void* user_data)
{
if (listen_socket){
printf("Listening on port https://localhost:%d\n", config.port);
globalListenSocket = listen_socket;
}else{
printf("Failed to listen on port https://localhost:%d\n", config.port);
}
}
int main()
{
/* Overly simple hello world app (SNI)*/
app = uws_create_app(SSL, (struct us_socket_context_options_t){
/* There are example certificates in uWebSockets.js repo */
.key_file_name = "../misc/key.pem",
.cert_file_name = "../misc/cert.pem",
.passphrase = "1234"
});
uws_missing_server_name(SSL, app, missing_server_name_handler, NULL);
uws_app_get(SSL, app, "/*", get_handler, NULL);
uws_app_get(SSL, app, "/exit", exit_handler, NULL);
uws_app_listen(SSL, app, 3000, listen_handler, NULL);
/* Let's add a wildcard SNI to begin with */
uws_add_server_name(SSL, app, "*.google.*");
uws_app_run(SSL, app);
}

View File

@@ -1,255 +0,0 @@
#include "../libuwebsockets.h"
#include "libusockets.h"
#include <stdio.h>
#include <malloc.h>
#include <string.h>
/* This is a simple WebSocket "sync" upgrade example.
* You may compile it with "WITH_OPENSSL=1 make" or with "make" */
#define SSL 1
typedef struct
{
char *value;
size_t length;
} header_t;
struct PerSocketData
{
/* Define your user data */
int something;
};
struct UpgradeData
{
header_t *secWebSocketKey;
header_t *secWebSocketProtocol;
header_t *secWebSocketExtensions;
uws_socket_context_t *context;
uws_res_t *response;
bool aborted;
};
header_t *create_header(size_t length, const char* value)
{
header_t *header = (header_t *)malloc(sizeof(header_t));
if(length > 0){
header->value = (char *)calloc(sizeof(char), length);
header->length = length;
memcpy(header->value, value, length);
}else{
header->value = NULL;
header->length = 0;
}
return header;
}
void free_header(header_t *header)
{
free(header->value);
free(header);
}
void listen_handler(struct us_listen_socket_t *listen_socket, uws_app_listen_config_t config, void *user_data)
{
if (listen_socket)
{
printf("Listening on port wss://localhost:%d\n", config.port);
}
}
//Timer close helper
void uws_timer_close(struct us_timer_t *timer)
{
struct us_timer_t *t = (struct us_timer_t *)timer;
struct timer_handler_data *data;
memcpy(&data, us_timer_ext(t), sizeof(struct timer_handler_data *));
free(data);
us_timer_close(t, 0);
}
//Timer create helper
struct us_timer_t *uws_create_timer(int ms, int repeat_ms, void (*handler)(void *data), void *data)
{
struct us_loop_t *loop = uws_get_loop();
struct us_timer_t *delayTimer = us_create_timer(loop, 0, sizeof(void *));
struct timer_handler_data
{
void *data;
void (*handler)(void *data);
bool repeat;
};
struct timer_handler_data *timer_data = (struct timer_handler_data *)malloc(sizeof(timer_handler_data));
timer_data->data = data;
timer_data->handler = handler;
timer_data->repeat = repeat_ms > 0;
memcpy(us_timer_ext(delayTimer), &timer_data, sizeof(struct timer_handler_data *));
us_timer_set(
delayTimer, [](struct us_timer_t *t)
{
/* We wrote the pointer to the timer's extension */
struct timer_handler_data *data;
memcpy(&data, us_timer_ext(t), sizeof(struct timer_handler_data *));
data->handler(data->data);
if (!data->repeat)
{
free(data);
us_timer_close(t, 0);
}
},
ms, repeat_ms);
return (struct us_timer_t *)delayTimer;
}
void on_timer_done(void *data)
{
struct UpgradeData *upgrade_data = (struct UpgradeData *)data;
/* Were'nt we aborted before our async task finished? Okay, upgrade then! */
if (!upgrade_data->aborted)
{
struct PerSocketData *socket_data = (struct PerSocketData *)malloc(sizeof(struct PerSocketData));
socket_data->something = 15;
printf("Async task done, upgrading to WebSocket now!\n");
uws_res_upgrade(SSL,
upgrade_data->response,
(void *)socket_data,
upgrade_data->secWebSocketKey->value,
upgrade_data->secWebSocketKey->length,
upgrade_data->secWebSocketProtocol->value,
upgrade_data->secWebSocketProtocol->length,
upgrade_data->secWebSocketExtensions->value,
upgrade_data->secWebSocketExtensions->length,
upgrade_data->context);
}
else
{
printf("Async task done, but the HTTP socket was closed. Skipping upgrade to WebSocket!\n");
}
free_header(upgrade_data->secWebSocketKey);
free_header(upgrade_data->secWebSocketProtocol);
free_header(upgrade_data->secWebSocketExtensions);
free(upgrade_data);
}
void on_res_aborted(uws_res_t *response, void *data)
{
struct UpgradeData *upgrade_data = (struct UpgradeData *)data;
/* We don't implement any kind of cancellation here,
* so simply flag us as aborted */
upgrade_data->aborted = true;
}
void upgrade_handler(uws_res_t *response, uws_req_t *request, uws_socket_context_t *context)
{
/* HttpRequest (req) is only valid in this very callback, so we must COPY the headers
* we need later on while upgrading to WebSocket. You must not access req after first return.
* Here we create a heap allocated struct holding everything we will need later on. */
struct UpgradeData *data = (struct UpgradeData *)malloc(sizeof(struct UpgradeData));
data->aborted = false;
data->context = context;
data->response = response;
const char *ws_key = NULL;
const char *ws_protocol = NULL;
const char *ws_extensions = NULL;
size_t ws_key_length = uws_req_get_header(request, "sec-websocket-key", 17, &ws_key);
size_t ws_protocol_length = uws_req_get_header(request, "sec-websocket-protocol", 22, &ws_protocol);
size_t ws_extensions_length = uws_req_get_header(request, "sec-websocket-extensions", 24, &ws_extensions);
data->secWebSocketKey = create_header(ws_key_length, ws_key);
data->secWebSocketProtocol = create_header(ws_protocol_length, ws_protocol);
data->secWebSocketExtensions = create_header(ws_extensions_length, ws_extensions);
/* We have to attach an abort handler for us to be aware
* of disconnections while we perform async tasks */
uws_res_on_aborted(SSL, response, on_res_aborted, data);
/* Simulate checking auth for 5 seconds. This looks like crap, never write
* code that utilize us_timer_t like this; they are high-cost and should
* not be created and destroyed more than rarely!
* Either way, here we go!*/
uws_create_timer(5000, 0, on_timer_done, data);
}
void open_handler(uws_websocket_t *ws)
{
/* Open event here, you may access uws_ws_get_user_data(ws) which points to a PerSocketData struct.
* Here we simply validate that indeed, something == 15 as set in upgrade handler. */
struct PerSocketData *data = (struct PerSocketData *)uws_ws_get_user_data(SSL, ws);
data->something = 15;
printf("Something is: %d\n", data->something);
}
void message_handler(uws_websocket_t *ws, const char *message, size_t length, uws_opcode_t opcode)
{
/* We simply echo whatever data we get */
uws_ws_send(SSL, ws, message, length, opcode);
}
void close_handler(uws_websocket_t *ws, int code, const char *message, size_t length)
{
/* You may access uws_ws_get_user_data(ws) here, but sending or
* doing any kind of I/O with the socket is not valid. */
struct PerSocketData *data = (struct PerSocketData *)uws_ws_get_user_data(SSL, ws);
if (data)
{
free(data);
}
}
void drain_handler(uws_websocket_t *ws)
{
/* Check uws_ws_get_buffered_amount(ws) here */
}
void ping_handler(uws_websocket_t *ws, const char *message, size_t length)
{
/* You don't need to handle this one, we automatically respond to pings as per standard */
}
void pong_handler(uws_websocket_t *ws, const char *message, size_t length)
{
/* You don't need to handle this one either */
}
int main()
{
uws_app_t *app = uws_create_app(SSL, (struct us_socket_context_options_t){
/* There are example certificates in uWebSockets.js repo */
.key_file_name = "../misc/key.pem",
.cert_file_name = "../misc/cert.pem",
.passphrase = "1234"
});
uws_ws(SSL, app, "/*", (uws_socket_behavior_t){
.compression = uws_compress_options_t::SHARED_COMPRESSOR,
.maxPayloadLength = 16 * 1024,
.idleTimeout = 12,
.maxBackpressure = 1 * 1024 * 1024,
.upgrade = upgrade_handler,
.open = open_handler,
.message = message_handler,
.drain = drain_handler,
.ping = ping_handler,
.pong = pong_handler,
.close = close_handler,
});
uws_app_listen(SSL, app, 9001, listen_handler, NULL);
uws_app_run(SSL, app);
}

View File

@@ -1,117 +0,0 @@
#include "../libuwebsockets.h"
#include <stdio.h>
#include <malloc.h>
#define SSL 1
/* This is a simple WebSocket "sync" upgrade example.
* You may compile it with "WITH_OPENSSL=1 make" or with "make" */
/* uws_ws_get_user_data(ws) returns one of these */
struct PerSocketData
{
/* Define your user data */
int something;
};
void listen_handler(struct us_listen_socket_t *listen_socket, uws_app_listen_config_t config, void *user_data)
{
if (listen_socket)
{
printf("Listening on port wss://localhost:%d\n", config.port);
}
}
void upgrade_handler(uws_res_t *response, uws_req_t *request, uws_socket_context_t *context)
{
/* You may read from req only here, and COPY whatever you need into your PerSocketData.
* PerSocketData is valid from .open to .close event, accessed with uws_ws_get_user_data(ws).
* HttpRequest (req) is ONLY valid in this very callback, so any data you will need later
* has to be COPIED into PerSocketData here. */
/* Immediately upgrading without doing anything "async" before, is simple */
struct PerSocketData *data = (struct PerSocketData *)malloc(sizeof(struct PerSocketData));
data->something = 15;
const char *ws_key = NULL;
const char *ws_protocol = NULL;
const char *ws_extensions = NULL;
size_t ws_key_length = uws_req_get_header(request, "sec-websocket-key", 17, &ws_key);
size_t ws_protocol_length = uws_req_get_header(request, "sec-websocket-protocol", 22, &ws_protocol);
size_t ws_extensions_length = uws_req_get_header(request, "sec-websocket-extensions", 24, &ws_extensions);
uws_res_upgrade(SSL,
response,
(void *)data,
ws_key,
ws_key_length,
ws_protocol,
ws_protocol_length,
ws_extensions,
ws_extensions_length,
context);
}
void open_handler(uws_websocket_t *ws)
{
/* Open event here, you may access uws_ws_get_user_data(ws) which points to a PerSocketData struct.
* Here we simply validate that indeed, something == 15 as set in upgrade handler. */
struct PerSocketData *data = (struct PerSocketData *)uws_ws_get_user_data(SSL, ws);
data->something = 15;
printf("Something is: %d\n", data->something);
}
void message_handler(uws_websocket_t *ws, const char *message, size_t length, uws_opcode_t opcode)
{
/* We simply echo whatever data we get */
uws_ws_send(SSL, ws, message, length, opcode);
}
void close_handler(uws_websocket_t *ws, int code, const char *message, size_t length)
{
/* You may access uws_ws_get_user_data(ws) here, but sending or
* doing any kind of I/O with the socket is not valid. */
struct PerSocketData *data = (struct PerSocketData *)uws_ws_get_user_data(SSL, ws);
if (data)
free(data);
}
void drain_handler(uws_websocket_t *ws)
{
/* Check uws_ws_get_buffered_amount(ws) here */
}
void ping_handler(uws_websocket_t *ws, const char *message, size_t length)
{
/* You don't need to handle this one, we automatically respond to pings as per standard */
}
void pong_handler(uws_websocket_t *ws, const char *message, size_t length)
{
/* You don't need to handle this one either */
}
int main()
{
uws_app_t *app = uws_create_app(SSL, (struct us_socket_context_options_t){
/* There are example certificates in uWebSockets.js repo */
.key_file_name = "../misc/key.pem",
.cert_file_name = "../misc/cert.pem",
.passphrase = "1234"
});
uws_ws(SSL, app, "/*", (uws_socket_behavior_t){.compression = uws_compress_options_t::SHARED_COMPRESSOR, .maxPayloadLength = 16 * 1024, .idleTimeout = 12, .maxBackpressure = 1 * 1024 * 1024, .upgrade = upgrade_handler, .open = open_handler, .message = message_handler, .drain = drain_handler, .ping = ping_handler, .pong = pong_handler, .close = close_handler});
uws_app_listen(SSL, app, 9001, listen_handler, NULL);
uws_app_run(SSL, app);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,260 +0,0 @@
/*
* Copyright 2022 Ciro Spaciari
*
* 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.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// clang-format off
#ifndef LIBUWS_CAPI_HEADER
#define LIBUWS_CAPI_HEADER
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "libusockets.h"
#ifdef __cplusplus
extern "C"
{
#endif
#ifdef _WIN32
# define DLL_EXPORT __declspec( dllexport )
#else
# define DLL_EXPORT
#endif
DLL_EXPORT typedef enum
{
/* These are not actual compression options */
_COMPRESSOR_MASK = 0x00FF,
_DECOMPRESSOR_MASK = 0x0F00,
/* Disabled, shared, shared are "special" values */
DISABLED = 0,
SHARED_COMPRESSOR = 1,
SHARED_DECOMPRESSOR = 1 << 8,
/* Highest 4 bits describe decompressor */
DEDICATED_DECOMPRESSOR_32KB = 15 << 8,
DEDICATED_DECOMPRESSOR_16KB = 14 << 8,
DEDICATED_DECOMPRESSOR_8KB = 13 << 8,
DEDICATED_DECOMPRESSOR_4KB = 12 << 8,
DEDICATED_DECOMPRESSOR_2KB = 11 << 8,
DEDICATED_DECOMPRESSOR_1KB = 10 << 8,
DEDICATED_DECOMPRESSOR_512B = 9 << 8,
/* Same as 32kb */
DEDICATED_DECOMPRESSOR = 15 << 8,
/* Lowest 8 bit describe compressor */
DEDICATED_COMPRESSOR_3KB = 9 << 4 | 1,
DEDICATED_COMPRESSOR_4KB = 9 << 4 | 2,
DEDICATED_COMPRESSOR_8KB = 10 << 4 | 3,
DEDICATED_COMPRESSOR_16KB = 11 << 4 | 4,
DEDICATED_COMPRESSOR_32KB = 12 << 4 | 5,
DEDICATED_COMPRESSOR_64KB = 13 << 4 | 6,
DEDICATED_COMPRESSOR_128KB = 14 << 4 | 7,
DEDICATED_COMPRESSOR_256KB = 15 << 4 | 8,
/* Same as 256kb */
DEDICATED_COMPRESSOR = 15 << 4 | 8
} uws_compress_options_t;
DLL_EXPORT typedef enum
{
CONTINUATION = 0,
TEXT = 1,
BINARY = 2,
CLOSE = 8,
PING = 9,
PONG = 10
} uws_opcode_t;
DLL_EXPORT typedef enum
{
BACKPRESSURE,
SUCCESS,
DROPPED
} uws_sendstatus_t;
DLL_EXPORT typedef struct
{
int port;
const char *host;
int options;
} uws_app_listen_config_t;
DLL_EXPORT typedef struct {
bool ok;
bool has_responded;
} uws_try_end_result_t;
DLL_EXPORT struct uws_app_s;
DLL_EXPORT struct uws_req_s;
DLL_EXPORT struct uws_res_s;
DLL_EXPORT struct uws_websocket_s;
DLL_EXPORT struct uws_header_iterator_s;
DLL_EXPORT typedef struct uws_app_s uws_app_t;
DLL_EXPORT typedef struct uws_req_s uws_req_t;
DLL_EXPORT typedef struct uws_res_s uws_res_t;
DLL_EXPORT typedef struct uws_socket_context_s uws_socket_context_t;
DLL_EXPORT typedef struct uws_websocket_s uws_websocket_t;
DLL_EXPORT typedef void (*uws_websocket_handler)(uws_websocket_t *ws, void* user_data);
DLL_EXPORT typedef void (*uws_websocket_message_handler)(uws_websocket_t *ws, const char *message, size_t length, uws_opcode_t opcode, void* user_data);
DLL_EXPORT typedef void (*uws_websocket_ping_pong_handler)(uws_websocket_t *ws, const char *message, size_t length, void* user_data);
DLL_EXPORT typedef void (*uws_websocket_close_handler)(uws_websocket_t *ws, int code, const char *message, size_t length, void* user_data);
DLL_EXPORT typedef void (*uws_websocket_upgrade_handler)(uws_res_t *response, uws_req_t *request, uws_socket_context_t *context, void* user_data);
DLL_EXPORT typedef void (*uws_websocket_subscription_handler)(uws_websocket_t *ws, const char *topic_name, size_t topic_name_length, int new_number_of_subscriber, int old_number_of_subscriber, void* user_data);
DLL_EXPORT typedef struct
{
uws_compress_options_t compression;
/* Maximum message size we can receive */
unsigned int maxPayloadLength;
/* 2 minutes timeout is good */
unsigned short idleTimeout;
/* 64kb backpressure is probably good */
unsigned int maxBackpressure;
bool closeOnBackpressureLimit;
/* This one depends on kernel timeouts and is a bad default */
bool resetIdleTimeoutOnSend;
/* A good default, esp. for newcomers */
bool sendPingsAutomatically;
/* Maximum socket lifetime in seconds before forced closure (defaults to disabled) */
unsigned short maxLifetime;
uws_websocket_upgrade_handler upgrade;
uws_websocket_handler open;
uws_websocket_message_handler message;
uws_websocket_handler drain;
uws_websocket_ping_pong_handler ping;
uws_websocket_ping_pong_handler pong;
uws_websocket_close_handler close;
uws_websocket_subscription_handler subscription;
} uws_socket_behavior_t;
DLL_EXPORT typedef void (*uws_listen_handler)(struct us_listen_socket_t *listen_socket, uws_app_listen_config_t config, void *user_data);
DLL_EXPORT typedef void (*uws_listen_domain_handler)(struct us_listen_socket_t *listen_socket, const char* domain, size_t domain_length, int options, void *user_data);
DLL_EXPORT typedef void (*uws_method_handler)(uws_res_t *response, uws_req_t *request, void *user_data);
DLL_EXPORT typedef void (*uws_filter_handler)(uws_res_t *response, int, void *user_data);
DLL_EXPORT typedef void (*uws_missing_server_handler)(const char *hostname, size_t hostname_length, void *user_data);
DLL_EXPORT typedef void (*uws_get_headers_server_handler)(const char *header_name, size_t header_name_size, const char *header_value, size_t header_value_size, void *user_data);
//Basic HTTP
DLL_EXPORT uws_app_t *uws_create_app(int ssl, struct us_bun_socket_context_options_t options);
DLL_EXPORT void uws_app_destroy(int ssl, uws_app_t *app);
DLL_EXPORT void uws_app_get(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
DLL_EXPORT void uws_app_post(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
DLL_EXPORT void uws_app_options(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
DLL_EXPORT void uws_app_delete(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
DLL_EXPORT void uws_app_patch(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
DLL_EXPORT void uws_app_put(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
DLL_EXPORT void uws_app_head(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
DLL_EXPORT void uws_app_connect(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
DLL_EXPORT void uws_app_trace(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
DLL_EXPORT void uws_app_any(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
DLL_EXPORT void uws_app_run(int ssl, uws_app_t *);
DLL_EXPORT void uws_app_listen(int ssl, uws_app_t *app, int port, uws_listen_handler handler, void *user_data);
DLL_EXPORT void uws_app_listen_with_config(int ssl, uws_app_t *app, uws_app_listen_config_t config, uws_listen_handler handler, void *user_data);
DLL_EXPORT void uws_app_listen_domain(int ssl, uws_app_t *app, const char *domain, size_t domain_length, uws_listen_domain_handler handler, void *user_data);
DLL_EXPORT void uws_app_listen_domain_with_options(int ssl, uws_app_t *app, const char *domain,size_t domain_length, int options, uws_listen_domain_handler handler, void *user_data);
DLL_EXPORT void uws_app_domain(int ssl, uws_app_t *app, const char* server_name, size_t server_name_length);
DLL_EXPORT bool uws_constructor_failed(int ssl, uws_app_t *app);
DLL_EXPORT unsigned int uws_num_subscribers(int ssl, uws_app_t *app, const char *topic, size_t topic_length);
DLL_EXPORT bool uws_publish(int ssl, uws_app_t *app, const char *topic, size_t topic_length, const char *message, size_t message_length, uws_opcode_t opcode, bool compress);
DLL_EXPORT void *uws_get_native_handle(int ssl, uws_app_t *app);
DLL_EXPORT void uws_remove_server_name(int ssl, uws_app_t *app, const char *hostname_pattern, size_t hostname_pattern_length);
DLL_EXPORT void uws_add_server_name(int ssl, uws_app_t *app, const char *hostname_pattern, size_t hostname_pattern_length);
DLL_EXPORT void uws_add_server_name_with_options(int ssl, uws_app_t *app, const char *hostname_pattern, size_t hostname_pattern_length, struct us_bun_socket_context_options_t options);
DLL_EXPORT void uws_missing_server_name(int ssl, uws_app_t *app, uws_missing_server_handler handler, void *user_data);
DLL_EXPORT void uws_filter(int ssl, uws_app_t *app, uws_filter_handler handler, void *user_data);
//WebSocket
DLL_EXPORT void uws_ws(int ssl, uws_app_t *app, const char *pattern, uws_socket_behavior_t behavior, void* user_data);
DLL_EXPORT void *uws_ws_get_user_data(int ssl, uws_websocket_t *ws);
DLL_EXPORT void uws_ws_close(int ssl, uws_websocket_t *ws);
DLL_EXPORT uws_sendstatus_t uws_ws_send(int ssl, uws_websocket_t *ws, const char *message, size_t length, uws_opcode_t opcode);
DLL_EXPORT uws_sendstatus_t uws_ws_send_with_options(int ssl, uws_websocket_t *ws, const char *message, size_t length, uws_opcode_t opcode, bool compress, bool fin);
DLL_EXPORT uws_sendstatus_t uws_ws_send_fragment(int ssl, uws_websocket_t *ws, const char *message, size_t length, bool compress);
DLL_EXPORT uws_sendstatus_t uws_ws_send_first_fragment(int ssl, uws_websocket_t *ws, const char *message, size_t length, bool compress);
DLL_EXPORT uws_sendstatus_t uws_ws_send_first_fragment_with_opcode(int ssl, uws_websocket_t *ws, const char *message, size_t length, uws_opcode_t opcode, bool compress);
DLL_EXPORT uws_sendstatus_t uws_ws_send_last_fragment(int ssl, uws_websocket_t *ws, const char *message, size_t length, bool compress);
DLL_EXPORT void uws_ws_end(int ssl, uws_websocket_t *ws, int code, const char *message, size_t length);
DLL_EXPORT void uws_ws_cork(int ssl, uws_websocket_t *ws, void (*handler)(void *user_data), void *user_data);
DLL_EXPORT bool uws_ws_subscribe(int ssl, uws_websocket_t *ws, const char *topic, size_t length);
DLL_EXPORT bool uws_ws_unsubscribe(int ssl, uws_websocket_t *ws, const char *topic, size_t length);
DLL_EXPORT bool uws_ws_is_subscribed(int ssl, uws_websocket_t *ws, const char *topic, size_t length);
DLL_EXPORT void uws_ws_iterate_topics(int ssl, uws_websocket_t *ws, void (*callback)(const char *topic, size_t length, void *user_data), void *user_data);
DLL_EXPORT bool uws_ws_publish(int ssl, uws_websocket_t *ws, const char *topic, size_t topic_length, const char *message, size_t message_length);
DLL_EXPORT bool uws_ws_publish_with_options(int ssl, uws_websocket_t *ws, const char *topic, size_t topic_length, const char *message, size_t message_length, uws_opcode_t opcode, bool compress);
DLL_EXPORT unsigned int uws_ws_get_buffered_amount(int ssl, uws_websocket_t *ws);
DLL_EXPORT size_t uws_ws_get_remote_address(int ssl, uws_websocket_t *ws, const char **dest);
DLL_EXPORT size_t uws_ws_get_remote_address_as_text(int ssl, uws_websocket_t *ws, const char **dest);
DLL_EXPORT void uws_res_get_remote_address_info(uws_res_t *res, const char **dest, size_t *length, unsigned int *port);
//Response
DLL_EXPORT void uws_res_end(int ssl, uws_res_t *res, const char *data, size_t length, bool close_connection);
DLL_EXPORT uws_try_end_result_t uws_res_try_end(int ssl, uws_res_t *res, const char *data, size_t length, uint64_t total_size, bool close_connection);
DLL_EXPORT void uws_res_cork(int ssl, uws_res_t *res, void(*callback)(uws_res_t *res, void* user_data) ,void* user_data);
DLL_EXPORT void uws_res_pause(int ssl, uws_res_t *res);
DLL_EXPORT void uws_res_resume(int ssl, uws_res_t *res);
DLL_EXPORT void uws_res_write_continue(int ssl, uws_res_t *res);
DLL_EXPORT void uws_res_write_status(int ssl, uws_res_t *res, const char *status, size_t length);
DLL_EXPORT void uws_res_write_header(int ssl, uws_res_t *res, const char *key, size_t key_length, const char *value, size_t value_length);
DLL_EXPORT void uws_res_write_header_int(int ssl, uws_res_t *res, const char *key, size_t key_length, uint64_t value);
DLL_EXPORT void uws_res_end_without_body(int ssl, uws_res_t *res, bool close_connection);
DLL_EXPORT bool uws_res_write(int ssl, uws_res_t *res, const char *data, size_t length);
DLL_EXPORT uint64_t uws_res_get_write_offset(int ssl, uws_res_t *res);
DLL_EXPORT void uws_res_override_write_offset(int ssl, uws_res_t *res, uint64_t offset);
DLL_EXPORT bool uws_res_has_responded(int ssl, uws_res_t *res);
DLL_EXPORT void uws_res_on_writable(int ssl, uws_res_t *res, bool (*handler)(uws_res_t *res, uint64_t, void *optional_data), void *user_data);
DLL_EXPORT void uws_res_on_aborted(int ssl, uws_res_t *res, void (*handler)(uws_res_t *res, void *optional_data), void *optional_data);
DLL_EXPORT void uws_res_on_data(int ssl, uws_res_t *res, void (*handler)(uws_res_t *res, const char *chunk, size_t chunk_length, bool is_end, void *optional_data), void *optional_data);
DLL_EXPORT void uws_res_upgrade(int ssl, uws_res_t *res, void *data, const char *sec_web_socket_key, size_t sec_web_socket_key_length, const char *sec_web_socket_protocol, size_t sec_web_socket_protocol_length, const char *sec_web_socket_extensions, size_t sec_web_socket_extensions_length, uws_socket_context_t *ws);
DLL_EXPORT size_t uws_res_get_remote_address(int ssl, uws_res_t *res, const char **dest);
DLL_EXPORT size_t uws_res_get_remote_address_as_text(int ssl, uws_res_t *res, const char **dest);
#ifdef UWS_WITH_PROXY
DLL_EXPORT size_t uws_res_get_proxied_remote_address(int ssl, uws_res_t *res, const char **dest);
DLL_EXPORT size_t uws_res_get_proxied_remote_address_as_text(int ssl, uws_res_t *res, const char **dest);
#endif
DLL_EXPORT void *uws_res_get_native_handle(int ssl, uws_res_t *res);
//Request
DLL_EXPORT bool uws_req_is_ancient(uws_req_t *res);
DLL_EXPORT bool uws_req_get_yield(uws_req_t *res);
DLL_EXPORT void uws_req_set_yield(uws_req_t *res, bool yield);
DLL_EXPORT size_t uws_req_get_url(uws_req_t *res, const char **dest);
DLL_EXPORT size_t uws_req_get_full_url(uws_req_t *res, const char **dest);
DLL_EXPORT size_t uws_req_get_method(uws_req_t *res, const char **dest);
DLL_EXPORT size_t uws_req_get_case_sensitive_method(uws_req_t *res, const char **dest);
DLL_EXPORT size_t uws_req_get_header(uws_req_t *res, const char *lower_case_header, size_t lower_case_header_length, const char **dest);
DLL_EXPORT void uws_req_for_each_header(uws_req_t *res, uws_get_headers_server_handler handler, void *user_data);
DLL_EXPORT size_t uws_req_get_query(uws_req_t *res, const char *key, size_t key_length, const char **dest);
DLL_EXPORT size_t uws_req_get_parameter(uws_req_t *res, unsigned short index, const char **dest);
DLL_EXPORT struct us_loop_t *uws_get_loop();
DLL_EXPORT struct us_loop_t *uws_get_loop_with_native(void* existing_native_loop);
DLL_EXPORT void uws_loop_defer(struct us_loop_t *loop, void( cb(void *user_data) ), void *user_data);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -124,7 +124,7 @@ private:
/* Signal broken HTTP request only if we have a pending request */
if (httpResponseData->onAborted) {
httpResponseData->onAborted();
httpResponseData->onAborted((HttpResponse<SSL> *)s, httpResponseData->userData);
}
/* Destruct socket ext */
@@ -258,7 +258,7 @@ private:
}
/* We might respond in the handler, so do not change timeout after this */
httpResponseData->inStream(data, fin);
httpResponseData->inStream(static_cast<HttpResponse<SSL>*>(user), data.data(), data.length(), fin, httpResponseData->userData);
/* Was the socket closed? */
if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) {
@@ -366,7 +366,7 @@ private:
/* We expect the developer to return whether or not write was successful (true).
* If write was never called, the developer should still return true so that we may drain. */
bool success = httpResponseData->callOnWritable(httpResponseData->offset);
bool success = httpResponseData->callOnWritable((HttpResponse<SSL> *)asyncSocket, httpResponseData->offset);
/* The developer indicated that their onWritable failed. */
if (!success) {

View File

@@ -558,10 +558,11 @@ public:
}
/* Attach handler for writable HTTP response */
HttpResponse *onWritable(MoveOnlyFunction<bool(uint64_t)> &&handler) {
HttpResponse *onWritable(void* userData, HttpResponseData<SSL>::OnWritableCallback handler) {
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
httpResponseData->onWritable = std::move(handler);
httpResponseData->userData = userData;
httpResponseData->onWritable = handler;
return this;
}
@@ -574,17 +575,31 @@ public:
}
/* Attach handler for aborted HTTP request */
HttpResponse *onAborted(MoveOnlyFunction<void()> &&handler) {
HttpResponse *onAborted(void* userData, HttpResponseData<SSL>::OnAbortedCallback handler) {
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
httpResponseData->onAborted = std::move(handler);
httpResponseData->userData = userData;
httpResponseData->onAborted = handler;
return this;
}
HttpResponse* clearOnWritableAndAborted() {
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
httpResponseData->onWritable = nullptr;
httpResponseData->onAborted = nullptr;
return this;
}
HttpResponse* clearOnAborted() {
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
httpResponseData->onAborted = nullptr;
return this;
}
/* Attach a read handler for data sent. Will be called with FIN set true if last segment. */
void onData(MoveOnlyFunction<void(std::string_view, bool)> &&handler) {
void onData(void* userData, HttpResponseData<SSL>::OnDataCallback handler) {
HttpResponseData<SSL> *data = getHttpResponseData();
data->inStream = std::move(handler);
data->userData = userData;
data->inStream = handler;
/* Always reset this counter here */
data->received_bytes_per_timeout = 0;

View File

@@ -33,6 +33,10 @@ struct HttpResponseData : AsyncSocketData<SSL>, HttpParser {
template <bool> friend struct HttpResponse;
template <bool> friend struct HttpContext;
public:
using OnWritableCallback = bool (*)(uWS::HttpResponse<SSL>*, uint64_t, void*);
using OnAbortedCallback = void (*)(uWS::HttpResponse<SSL>*, void*);
using OnDataCallback = void (*)(uWS::HttpResponse<SSL>* response, const char* chunk, size_t chunk_length, bool, void*);
/* When we are done with a response we mark it like so */
void markDone() {
onAborted = nullptr;
@@ -46,15 +50,15 @@ struct HttpResponseData : AsyncSocketData<SSL>, HttpParser {
}
/* Caller of onWritable. It is possible onWritable calls markDone so we need to borrow it. */
bool callOnWritable(uint64_t offset) {
bool callOnWritable( uWS::HttpResponse<SSL>* response, uint64_t offset) {
/* Borrow real onWritable */
MoveOnlyFunction<bool(uint64_t)> borrowedOnWritable = std::move(onWritable);
auto* borrowedOnWritable = std::move(onWritable);
/* Set onWritable to placeholder */
onWritable = [](uint64_t) {return true;};
onWritable = [](uWS::HttpResponse<SSL>*, uint64_t, void*) {return true;};
/* Run borrowed onWritable */
bool ret = borrowedOnWritable(offset);
bool ret = borrowedOnWritable(response, offset, userData);
/* If we still have onWritable (the placeholder) then move back the real one */
if (onWritable) {
@@ -74,10 +78,13 @@ struct HttpResponseData : AsyncSocketData<SSL>, HttpParser {
HTTP_CONNECTION_CLOSE = 16 // used
};
/* Shared context pointer */
void* userData = nullptr;
/* Per socket event handlers */
MoveOnlyFunction<bool(uint64_t)> onWritable;
MoveOnlyFunction<void()> onAborted;
MoveOnlyFunction<void(std::string_view, bool)> inStream; // onData
OnWritableCallback onWritable = nullptr;
OnAbortedCallback onAborted = nullptr;
OnDataCallback inStream = nullptr;
/* Outgoing offset */
uint64_t offset = 0;

View File

@@ -95,13 +95,14 @@ private:
// This is both a performance thing, and also to prevent freeing some things which are not meant to be freed
// such as uv_tty_t
if(loop && cleanMe && !bun_is_exiting()) {
cleanMe = false;
loop->free();
}
}
Loop *loop = nullptr;
bool cleanMe = false;
};
static LoopCleaner &getLazyLoop() {
static thread_local LoopCleaner lazyLoop;
return lazyLoop;
@@ -126,6 +127,12 @@ public:
return getLazyLoop().loop;
}
static void clearLoopAtThreadExit() {
if (getLazyLoop().cleanMe) {
getLazyLoop().loop->free();
}
}
/* Freeing the default loop should be done once */
void free() {
LoopData *loopData = (LoopData *) us_loop_ext((us_loop_t *) this);

View File

@@ -20,6 +20,8 @@
#ifndef UWS_PERMESSAGEDEFLATE_H
#define UWS_PERMESSAGEDEFLATE_H
#define UWS_USE_LIBDEFLATE 1
#include <cstdint>
#include <cstring>
@@ -134,6 +136,9 @@ struct ZlibContext {
struct DeflationStream {
z_stream deflationStream = {};
#ifdef UWS_USE_LIBDEFLATE
unsigned char reset_buffer[4096 + 1];
#endif
DeflationStream(CompressOptions compressOptions) {
@@ -154,13 +159,11 @@ struct DeflationStream {
/* Run a fast path in case of shared_compressor */
if (reset) {
size_t written = 0;
static unsigned char buf[1024 + 1];
written = libdeflate_deflate_compress(zlibContext->compressor, raw.data(), raw.length(), buf, 1024);
written = libdeflate_deflate_compress(zlibContext->compressor, raw.data(), raw.length(), reset_buffer, 4096);
if (written) {
memcpy(&buf[written], "\x00", 1);
return std::string_view((char *) buf, written + 1);
memcpy(&reset_buffer[written], "\x00", 1);
return std::string_view((char *) reset_buffer, written + 1);
}
}
#endif
@@ -214,6 +217,9 @@ struct DeflationStream {
struct InflationStream {
z_stream inflationStream = {};
#ifdef UWS_USE_LIBDEFLATE
char buf[4096];
#endif
InflationStream(CompressOptions compressOptions) {
/* Inflation windowBits are the top 8 bits of the 16 bit compressOptions */
@@ -230,13 +236,12 @@ struct InflationStream {
#ifdef UWS_USE_LIBDEFLATE
/* Try fast path first */
size_t written = 0;
static char buf[1024];
/* We have to pad 9 bytes and restore those bytes when done since 9 is more than 6 of next WebSocket message */
char tmp[9];
memcpy(tmp, (char *) compressed.data() + compressed.length(), 9);
memcpy((char *) compressed.data() + compressed.length(), "\x00\x00\xff\xff\x01\x00\x00\xff\xff", 9);
libdeflate_result res = libdeflate_deflate_decompress(zlibContext->decompressor, compressed.data(), compressed.length() + 9, buf, 1024, &written);
libdeflate_result res = libdeflate_deflate_decompress(zlibContext->decompressor, compressed.data(), compressed.length() + 9, buf, 4096, &written);
memcpy((char *) compressed.data() + compressed.length(), tmp, 9);
if (res == 0) {

View File

@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// clang-format off
#ifndef UWS_WEBSOCKETCONTEXT_H
#define UWS_WEBSOCKETCONTEXT_H
@@ -270,7 +270,7 @@ private:
webSocketData->subscriber = nullptr;
if (webSocketContextData->closeHandler) {
webSocketContextData->closeHandler((WebSocket<SSL, isServer, USERDATA> *) s, 1006, {(char *) reason, (size_t) code});
webSocketContextData->closeHandler((WebSocket<SSL, isServer, USERDATA> *) s, 1006, reason != NULL && code > 0 ? std::string_view{(char *) reason, (size_t) code} : std::string_view());
}
}

View File

@@ -26,7 +26,7 @@
#include <string_view>
// bun-specific
#include "simdutf.h"
#include "wtf/SIMDUTF.h"
namespace uWS {

View File

@@ -3,9 +3,9 @@ param(
)
$ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot "env.ps1")
if ($env:CI) {
. (Join-Path $PSScriptRoot "env.ps1")
if ($env:CI -eq "true") {
& (Join-Path $PSScriptRoot "update-submodules.ps1")
}
@@ -79,6 +79,10 @@ Build-Dependency `
-Script "lshpack" `
-Outputs @("lshpack.lib")
Build-Dependency `
-Script "libdeflate" `
-Outputs @("deflate.lib")
if (!($Script:DidAnything)) {
Write-Host "(run with -Force to rebuild all)"
}

View File

@@ -3,7 +3,7 @@ set -eo pipefail
source "$(dirname -- "${BASH_SOURCE[0]}")/env.sh"
if [[ "$CI" ]]; then
$(dirname -- "${BASH_SOURCE[0]}")/update-submodules.sh
$(dirname -- "${BASH_SOURCE[0]}")/update-submodules.sh
fi
FORCE=
@@ -36,9 +36,11 @@ fi
dep() {
local submodule="$1"
local script="$2"
CACHE_KEY=
if [ "$CACHE" == "1" ]; then
CACHE_KEY="$submodule/$(echo "$SUBMODULES" | grep "$submodule" | git hash-object --stdin)"
local hash="$(echo "$SUBMODULES" | grep "$submodule" | awk '{print $1}')"
local os="$(uname -s | tr '[:upper:]' '[:lower:]')"
local arch="$(uname -m)"
CACHE_KEY="$submodule/$hash-$os-$arch-$CPU_TARGET"
fi
if [ -z "$FORCE" ]; then
HAS_ALL_DEPS=1
@@ -92,6 +94,7 @@ dep mimalloc mimalloc libmimalloc.a libmimalloc.o
dep tinycc tinycc libtcc.a
dep zlib zlib libz.a
dep zstd zstd libzstd.a
dep libdeflate libdeflate libdeflate.a
dep ls-hpack lshpack liblshpack.a
if [ "$BUILT_ANY" -eq 0 ]; then

View File

@@ -3,6 +3,7 @@ $ErrorActionPreference = 'Stop' # Setting strict mode, similar to 'set -euo pip
Push-Location (Join-Path $BUN_DEPS_DIR 'boringssl')
try {
Remove-Item -ErrorAction SilentlyContinue -Recurse -Force build
Set-Location (mkdir -Force build)
Run cmake @CMAKE_FLAGS ..

View File

@@ -6,7 +6,7 @@ cd $BUN_DEPS_DIR/boringssl
mkdir -p build
cd build
cmake "${CMAKE_FLAGS[@]}" -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" -GNinja ..
cmake "${CMAKE_FLAGS[@]}" -GNinja ..
ninja libcrypto.a libssl.a libdecrepit.a
cp **/libcrypto.a $BUN_DEPS_OUT_DIR/libcrypto.a

View File

@@ -1,29 +1,29 @@
param (
[switch] $Baseline = $False,
[switch] $Fast = $False
)
$ErrorActionPreference = 'Stop' # Setting strict mode, similar to 'set -euo pipefail' in bash
$Tag = If ($Baseline) { "-Baseline" } Else { "" }
$UseBaselineBuild = If ($Baseline) { "ON" } Else { "OFF" }
$UseLto = If ($Fast) { "OFF" } Else { "ON" }
. (Join-Path $PSScriptRoot "env.ps1")
if ($env:CI -eq "true") {
$env:FORCE_UPDATE_SUBMODULES = "1"
& (Join-Path $PSScriptRoot "update-submodules.ps1")
& (Join-Path $PSScriptRoot "build-libuv.ps1") -CloneOnly $True
}
# $CANARY_REVISION = if (Test-Path build/.canary_revision) { Get-Content build/.canary_revision } else { "0" }
$CANARY_REVISION = 0
.\scripts\env.ps1 $Tag
.\scripts\update-submodules.ps1
.\scripts\build-libuv.ps1 -CloneOnly $True
cd build
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release `
cmake .. @CMAKE_FLAGS `
-G Ninja `
-DCMAKE_BUILD_TYPE=Release `
-DNO_CODEGEN=0 `
-DNO_CONFIGURE_DEPENDS=1 `
"-DUSE_BASELINE_BUILD=${UseBaselineBuild}" `
"-DUSE_LTO=${UseLto}" `
"-DCANARY=${CANARY_REVISION}" `
-DBUN_CPP_ONLY=1 $Flags
-DBUN_CPP_ONLY=1
if ($LASTEXITCODE -ne 0) { throw "CMake configuration failed" }
.\compile-cpp-only.ps1 -v
if ($LASTEXITCODE -ne 0) { throw "C++ compilation failed" }
.\compile-cpp-only.ps1 -v -j $env:CPUS
if ($LASTEXITCODE -ne 0) { throw "C++ compilation failed" }
# HACK: For some reason, the buildkite agent is hanging when uploading bun-cpp-objects.a
# Best guess is that there is an issue when uploading files larger than 500 MB
#
# For now, use FileSplitter to split the file into smaller chunks:
# https://www.powershellgallery.com/packages/FileSplitter/1.3
if ($env:BUILDKITE) {
Split-File -Path (Resolve-Path "bun-cpp-objects.a") -PartSizeBytes "50MB" -Verbose
}

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