Repository: uNetworking/uWebSockets
Branch: master
Commit: 2d18f69d6288
Files: 115
Total size: 567.0 KB
Directory structure:
gitextract_q0soit6x/
├── .github/
│ └── workflows/
│ ├── codeql.yml
│ └── cpp.yml
├── .gitmodules
├── GNUmakefile
├── LICENSE
├── Makefile
├── README.md
├── autobahn/
│ ├── fuzzingclient.json
│ └── server-test.js
├── benchmarks/
│ ├── Makefile
│ ├── README.md
│ ├── broadcast_test.c
│ ├── load_test.c
│ ├── parser.cpp
│ └── scale_test.c
├── build.c
├── build.h
├── cluster/
│ └── README.md
├── examples/
│ ├── Broadcast.cpp
│ ├── BroadcastingEchoServer.cpp
│ ├── CachingApp.cpp
│ ├── Client.cpp
│ ├── Crc32.cpp
│ ├── EchoBody.cpp
│ ├── EchoServer.cpp
│ ├── EchoServerThreaded.cpp
│ ├── HelloWorld.cpp
│ ├── HelloWorldThreaded.cpp
│ ├── Http3Server.cpp
│ ├── HttpServer.cpp
│ ├── ParameterRoutes.cpp
│ ├── Precompress.cpp
│ ├── README.md
│ ├── SecureGzipFileServer.cpp
│ ├── ServerName.cpp
│ ├── SmokeTest.cpp
│ ├── UpgradeAsync.cpp
│ ├── UpgradeSync.cpp
│ └── helpers/
│ ├── AsyncFileReader.h
│ ├── AsyncFileStreamer.h
│ ├── Middleware.h
│ └── optparse.h
├── fuzzing/
│ ├── AsyncEpollHelloWorld.cpp
│ ├── AsyncEpollHelloWorld.dict
│ ├── EpollEchoServer.cpp
│ ├── EpollEchoServer.dict
│ ├── EpollEchoServerPubSub.cpp
│ ├── EpollEchoServerPubSub.dict
│ ├── EpollHelloWorld.cpp
│ ├── EpollHelloWorld.dict
│ ├── Extensions.cpp
│ ├── Http.cpp
│ ├── Makefile
│ ├── MultipartParser.cpp
│ ├── PerMessageDeflate.cpp
│ ├── QueryParser.cpp
│ ├── QueryParser.dict
│ ├── README.md
│ ├── TopicTree.cpp
│ ├── TopicTree.dict
│ ├── WebSocket.cpp
│ └── helpers.h
├── libEpollBenchmarker/
│ ├── Makefile
│ └── epoll_benchmarker.cpp
├── misc/
│ ├── READMORE.md
│ ├── cert.pem
│ └── key.pem
├── src/
│ ├── App.h
│ ├── AsyncSocket.h
│ ├── AsyncSocketData.h
│ ├── BloomFilter.h
│ ├── CachingApp.h
│ ├── ChunkedEncoding.h
│ ├── ClientApp.h
│ ├── Http3App.h
│ ├── Http3Context.h
│ ├── Http3ContextData.h
│ ├── Http3Request.h
│ ├── Http3Response.h
│ ├── Http3ResponseData.h
│ ├── HttpContext.h
│ ├── HttpContextData.h
│ ├── HttpErrors.h
│ ├── HttpParser.h
│ ├── HttpResponse.h
│ ├── HttpResponseData.h
│ ├── HttpRouter.h
│ ├── LocalCluster.h
│ ├── Loop.h
│ ├── LoopData.h
│ ├── MessageParser.h
│ ├── MoveOnlyFunction.h
│ ├── Multipart.h
│ ├── PerMessageDeflate.h
│ ├── ProxyParser.h
│ ├── QueryParser.h
│ ├── TopicTree.h
│ ├── Utilities.h
│ ├── WebSocket.h
│ ├── WebSocketContext.h
│ ├── WebSocketContextData.h
│ ├── WebSocketData.h
│ ├── WebSocketExtensions.h
│ ├── WebSocketHandshake.h
│ └── WebSocketProtocol.h
└── tests/
├── BloomFilter.cpp
├── ChunkedEncoding.cpp
├── ExtensionsNegotiator.cpp
├── HttpParser.cpp
├── HttpRouter.cpp
├── Makefile
├── Query.cpp
├── README.md
├── TopicTree.cpp
└── smoke.mjs
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/codeql.yml
================================================
name: "CodeQL"
on:
push:
branches: [ 'master' ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ 'master' ]
schedule:
- cron: '0 17 * * 2'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp', 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
queries: +security-and-quality
- run: |
echo "Fetch submodules"
git submodule update --init --recursive
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"
================================================
FILE: .github/workflows/cpp.yml
================================================
name: C++ CI
on: [push]
jobs:
short_fuzzing:
runs-on: ubuntu-latest
steps:
- name: Build fuzzers
id: build
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
with:
oss-fuzz-project-name: 'uwebsockets'
language: c++
- name: Run fuzzers
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'uwebsockets'
language: c++
fuzz-seconds: 600
- name: Upload crash
uses: actions/upload-artifact@v4
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
path: ./out/artifacts
build_windows:
runs-on: windows-latest
steps:
- name: Clone source
run: git clone --recursive https://github.com/uNetworking/uWebSockets.git
- name: Install libuv
run: |
vcpkg install libuv:x64-windows
cp C:\vcpkg\installed\x64-windows\bin\uv.dll uWebSockets\uv.dll
- uses: ilammy/msvc-dev-cmd@v1
- name: Build examples
run: |
cd uWebSockets
$Env:WITH_ZLIB='0'; $ENV:WITH_LTO='0'; $Env:CC='clang';
$ENV:CFLAGS='-I C:\vcpkg\installed\x64-windows\include';
$ENV:LDFLAGS='-L C:\vcpkg\installed\x64-windows\lib';
$ENV:CXX='clang++'; $ENV:EXEC_SUFFIX='.exe'; $ENV:WITH_LIBUV='1'; nmake
ls
- name: Run smoke test
run: |
cd uWebSockets
iwr https://deno.land/x/install/install.ps1 -useb | iex
Start-Process -NoNewWindow .\Crc32
sleep 1
deno run --allow-net tests\smoke.mjs
Stop-Process -Name Crc32
build_linux:
runs-on: ubuntu-latest
steps:
- name: Clone source
run: git clone --recursive https://github.com/uNetworking/uWebSockets.git
- name: Build source
run: cmake -S uWebSockets/libdeflate -B uWebSockets/libdeflate && make -C uWebSockets/libdeflate && WITH_ASAN=1 WITH_LIBDEFLATE=1 make -C uWebSockets
- name: List binaries
run: ls uWebSockets
- name: Install Deno
run: curl -fsSL https://deno.land/x/install/install.sh | sh
- name: Run smoke test
run: make -C uWebSockets/tests smoke
- name: Run compliance test
run: make -C uWebSockets/tests compliance
- name: Run unit tests
run: make -C uWebSockets/tests
- name: Autobahn|Testsuite
run: ~/.deno/bin/deno run -A --unstable uWebSockets/autobahn/server-test.js
build_osx:
runs-on: macos-latest
steps:
- name: Clone source
run: git clone --recursive https://github.com/uNetworking/uWebSockets.git
- name: Build source
run: make -C uWebSockets
- name: List binaries
run: ls uWebSockets
- name: Install Deno
run: curl -fsSL https://deno.land/x/install/install.sh | sh
- name: Run smoke test
run: make -C uWebSockets/tests smoke
- name: Run unit tests
run: make -C uWebSockets/tests
================================================
FILE: .gitmodules
================================================
[submodule "uSockets"]
path = uSockets
url = https://github.com/uNetworking/uSockets.git
[submodule "fuzzing/libEpollFuzzer"]
path = fuzzing/libEpollFuzzer
url = https://github.com/uNetworking/libEpollFuzzer
[submodule "fuzzing/seed-corpus"]
path = fuzzing/seed-corpus
url = https://github.com/uNetworking/seed-corpus.git
[submodule "h1spec"]
path = h1spec
url = https://github.com/uNetworking/h1spec
[submodule "libdeflate"]
path = libdeflate
url = https://github.com/ebiggers/libdeflate
================================================
FILE: GNUmakefile
================================================
# This is the GNU Make shim for Linux and macOS
DESTDIR ?=
prefix ?= /usr/local
examples: default
./build examples
clean: default
./build clean
capi: default
./build capi
install:
mkdir -p "$(DESTDIR)$(prefix)/include/uWebSockets"
cp -r src/* "$(DESTDIR)$(prefix)/include/uWebSockets"
all: default
./build all
default:
$(MAKE) -C uSockets
$(CC) -fopenmp build.c -o build || $(CC) build.c -o build
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: Makefile
================================================
# This is the NMake shim for Windows (WIP)
# Example: $Env:WITH_ZLIB='0'; $ENV:WITH_LTO='0'; $Env:CC='clang'; $ENV:CFLAGS='-I C:\vcpkg\installed\x64-windows\include'; $ENV:LDFLAGS='-L C:\vcpkg\installed\x64-windows\lib'; $ENV:CXX='clang++'; $ENV:EXEC_SUFFIX='.exe'; $ENV:WITH_LIBUV='1'; nmake
examples: default
.\build.exe examples || .\a.exe examples
clean: default
.\build.exe clean || .\a.exe clean
capi: default
.\build.exe capi || .\a.exe capi
all: default
.\build.exe all || .\a.exe all
default:
cd uSockets
$(CC) $(CFLAGS) -DLIBUS_NO_SSL -std=c11 -Isrc -O3 -c src/*.c src/eventing/*.c src/crypto/*.c
cd ..
$(CC) build.c
================================================
FILE: README.md
================================================
Simple, secure1 & standards compliant2 web server for the most demanding3 of applications. Read more...
### :closed_lock_with_key: Optimized security
Being meticulously optimized for speed and memory footprint, µWebSockets is fast enough to do encrypted TLS 1.3 messaging quicker than most alternative servers can do even unencrypted, cleartext messaging3.
Furthermore, we partake in Google's OSS-Fuzz with a ~95% daily fuzzing coverage4 with no sanitizer issues. LGTM scores us flawless A+ from having zero CodeQL alerts and we compile with pedantic warning levels.
### :arrow_forward: Rapid scripting
µWebSockets is written entirely in C & C++ but has a seamless integration for Node.js backends. This allows for rapid scripting of powerful apps, using widespread competence. See µWebSockets.js.
### :crossed_swords: Battle proven
We've been fully standards compliant with a perfect Autobahn|Testsuite score since 20162. µWebSockets powers many of the biggest crypto exchanges in the world, handling trade volumes of multiple billions of USD every day. If you trade crypto, chances are you do so via µWebSockets.
### :battery: Batteries included
Designed around a convenient URL router with wildcard & parameter support - paired with efficient pub/sub features for WebSockets. µWebSockets should be the obvious, complete starting point for any real-time web project with high demands.
Start building your Http & WebSocket apps in no time; read the user manual and see examples. You can browse our TypeDoc for a quick overview.
```c++
/* One app per thread; spawn as many as you have CPU-cores and let uWS share the listening port */
uWS::SSLApp({
/* These are the most common options, fullchain and key. See uSockets for more options. */
.cert_file_name = "cert.pem",
.key_file_name = "key.pem"
}).get("/hello/:name", [](auto *res, auto *req) {
/* You can efficiently stream huge files too */
res->writeStatus("200 OK")
->writeHeader("Content-Type", "text/html; charset=utf-8")
->write("Hello ")
->write(req->getParameter("name"))
->end("!
");
}).ws("/*", {
/* Just a few of the available handlers */
.open = [](auto *ws) {
ws->subscribe("oh_interesting_subject");
},
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
ws->send(message, opCode);
}
}).listen(9001, [](auto *listenSocket) {
if (listenSocket) {
std::cout << "Listening on port " << 9001 << std::endl;
} else {
std::cout << "Failed to load certs or to bind to port" << std::endl;
}
}).run();
```
### :briefcase: Commercially supported
uNetworking AB is a Swedish consulting & contracting company dealing with anything related to µWebSockets; development, support and customer success.
Don't hesitate sending a mail if you're building something large, in need of advice or having other business inquiries in mind. We'll figure out what's best for both parties and make sure you're not falling into common pitfalls.
Special thanks to BitMEX, Bitfinex, Google, Coinbase, Bitwyre, AppDrag and deepstreamHub for allowing the project itself to thrive on GitHub since 2016 - this project would not be possible without these beautiful companies.
### :wrench: Customizable architecture
µWebSockets builds on µSockets, a foundation library implementing eventing, networking and cryptography in three different layers. Every layer has multiple implementations and you control the compiled composition with flags. There are currently five event-loop integrations; libuv, ASIO, GCD and raw epoll/kqueue.
In a nutshell:
* `WITH_WOLFSSL=1 WITH_LIBUV=1 make examples` builds examples utilizing WolfSSL and libuv
* `WITH_OPENSSL=1 make examples` builds examples utilizing OpenSSL and the native kernel
See µSockets for an up-to-date list of flags and a more detailed explanation.
### :handshake: Permissively licensed
Intellectual property, all rights reserved.
Where such explicit notice is given, source code is licensed Apache License 2.0 which is a permissive OSI-approved license with very few limitations. Modified "forks" should be of nothing but licensed source code, and be made available under another product name. If you're uncertain about any of this, please ask before assuming.
================================================
FILE: autobahn/fuzzingclient.json
================================================
{
"outdir": "./reports/servers",
"servers": [
{
"agent": "uWebSockets",
"url": "ws://localhost:9001"
}
],
"cases": [
"1.*",
"2.*",
"3.*",
"4.*",
"5.*",
"6.*",
"7.*",
"9.*",
"10.*",
"11.*",
"12.*",
"13.*"
],
"exclude-cases": [
],
"exclude-agent-cases": {}
}
================================================
FILE: autobahn/server-test.js
================================================
// Based on https://github.com/denoland/fastwebsockets/blob/main/autobahn/server-test.js
import { $ } from "https://deno.land/x/dax/mod.ts";
const pwd = new URL(".", import.meta.url).pathname;
const AUTOBAHN_TESTSUITE_DOCKER =
"crossbario/autobahn-testsuite:0.8.2@sha256:519915fb568b04c9383f70a1c405ae3ff44ab9e35835b085239c258b6fac3074";
const server = $`uWebSockets/EchoServer`.spawn();
await $`docker run --name fuzzingserver -v ${pwd}/fuzzingclient.json:/fuzzingclient.json:ro -v ${pwd}/reports:/reports -p 9001:9001 --net=host --rm ${AUTOBAHN_TESTSUITE_DOCKER} wstest -m fuzzingclient -s fuzzingclient.json`.cwd(pwd);
const result = Object.values(JSON.parse(Deno.readTextFileSync("./uWebSockets/autobahn/reports/servers/index.json"))["uWebSockets"]);
function failed(name) {
return name != "OK" && name != "INFORMATIONAL" && name != "NON-STRICT";
}
console.log(JSON.stringify(result, null, 2));
const failedtests = result.filter((outcome) => failed(outcome.behavior) || failed(outcome.behaviorClose));
console.log(
`%c${result.length - failedtests.length} / ${result.length} tests OK`,
`color: ${failedtests.length == 0 ? "green" : "red"}`,
);
Deno.exit(failedtests.length == 0 ? 0 : 1);
================================================
FILE: benchmarks/Makefile
================================================
default:
g++ -flto -march=native parser.cpp -O3 -I../uSockets/src -o parser
clang -flto -O3 -DLIBUS_USE_OPENSSL -I../uSockets/src ../uSockets/src/*.c ../uSockets/src/eventing/*.c ../uSockets/src/crypto/*.c broadcast_test.c load_test.c scale_test.c -c
clang++ -flto -O3 -DLIBUS_USE_OPENSSL -I../uSockets/src ../uSockets/src/crypto/*.cpp -c -std=c++17
clang++ -flto -O3 -DLIBUS_USE_OPENSSL `ls *.o | grep -Ev "load_test|scale_test"` -lssl -lcrypto -o broadcast_test
clang++ -flto -O3 -DLIBUS_USE_OPENSSL `ls *.o | grep -Ev "broadcast_test|scale_test"` -lssl -lcrypto -lz -o load_test
clang++ -flto -O3 -DLIBUS_USE_OPENSSL `ls *.o | grep -Ev "broadcast_test|load_test"` -lssl -lcrypto -o scale_test
================================================
FILE: benchmarks/README.md
================================================
# More recent benchmarks
It was a while ago this page was written (and it needs updating), so here are the most recent benchmarks against the top competition we know of:
v20 | v21
--- | ---
 | 
# Benchmark-driven development
Making decisions based on scientific benchmarking **while** you develop can guide you to create very efficient solutions if you have the dicipline to follow through. µWebSockets performs with **98%** the theoretical maximum for any user space Linux process [this was written before io_uring was added to Linux] - if anything would ever be faster, it would only be so by less than 2%. We know of no such project.
Http | WebSockets
--- | ---
 | 
Because of the big lead in cleartext performance, it's actually possible to enable TLS 1.3 encryption in µWebSockets and still beat most of the competition in an unfair cleartext-vs-encrypted run. Performance retention of TLS 1.3 encryption with µWebSockets is about 60%, so you do the math.
All of this is possible thanks to extensive benchmarking of many discarded prototypes & designs during development. The very first thing done in this project was to benchmark the Linux kernel against itself, to get a clear idea of expected maximum performance and thus a performance budget on this platform.
From that point every line of code was benchmarked against the budget and thrown away if it failed the vision. Today µWebSockets does WebSocket messaging without any significant overhead, making it very unlikely to ever be outperformed.
Of course, memory usage has always been a big factor in this. The name µWebSockets is meant to signify "small WebSockets" and comes from the memory optimizations made throughout. Holding many WebSockets should not require lots of RAM.
If you're looking for a performant solution, look no further.
## Common benchmarking mistakes
It is very common, extremely common in fact, that people try and benchmark µWebSockets using a scripted Node.js client such as autocannon, ws, or anything similar. It might seem like an okay method but it really isn't. µWebSockets is 12x faster than Node.js, so trying to stress µWebSockets using Node.js is almost impossible. Maybe if you have a 16-core CPU and dedicate 15 cores to Node.js and 1 core to µWebSockets.
So whatever you do, it is of greatest importance that you actually **do check and make sure that µWebSockets is being stressed to 100% CPU-time** before noting the result. If it isn't, then you're not really benchmarking µWebSockets - you're benchmarking your client, trying to stress µWebSockets! Please don't make this mistake.
## Why "hello world" benchmarking?
Contrary to popular belief, "hello world benchmarks" are the most accurate and realistic gauges of performance for the kind of applications µWebSockets is designed for:
* IO-gaming (latency)
* Signalling (memory overhead)
* Trading (latency)
* Finance (latency)
* Chatting (memory overhead)
* Notifications (memory overhead)
Most business applications of the above mentioned categories are implemented without a central on-disk DB, blocking or severely limiting hot-path performance. As such, web IO becomes a significant part of overall bottleneck, if not the only bottleneck. Message echoing of around 1-16 kB or even as small as 512 bytes is a good test of the overall server plumbing (receive -> timeout clear -> emit to app -> timeout set -> send) for these applications.
Of course, if you build an app that *absolutely must* have an on-disk SQL DB central to all hot-paths, then µWebSockets is not the right tool for your app. Keep in mind that, finding a case where µWebSockets makes no difference, does not mean µWebSockets never makes a difference.
================================================
FILE: benchmarks/broadcast_test.c
================================================
/* This benchmark establishes _connections_ number of WebSocket
clients, then iteratively performs the following:
1. Send one message for every client.
2. Wait for the quadratic (_connections_^2) amount of responses from the server.
3. Once received all expected bytes, repeat by going to step 1.
Every 4 seconds we print the current average "iterations per second".
*/
#include
int SSL;
#include
#include
#include
unsigned char web_socket_request[26] = {130, 128 | 20, 1, 2, 3, 4};
char request[] = "GET / HTTP/1.1\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"
"Host: server.example.com\r\n"
"Sec-WebSocket-Version: 13\r\n\r\n";
char *host;
int port;
int connections;
int satisfied_sockets;
int iterations;
struct http_socket {
/* How far we have streamed our websocket request */
int offset;
/* How far we have streamed our upgrade request */
int upgrade_offset;
/* Are we upgraded? */
int is_upgraded;
/* Bytes received */
int bytes_received;
};
/* We track upgraded websockets */
void **web_sockets;
int num_web_sockets;
/* We don't need any of these */
void noop(struct us_loop_t *loop) {
}
void start_iteration() {
for (int i = 0; i < num_web_sockets; i++) {
struct us_socket_t *s = (struct us_socket_t *) web_sockets[i];
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
http_socket->offset = us_socket_write(SSL, s, (char *) web_socket_request, sizeof(web_socket_request), 0);
}
}
void next_connection(struct us_socket_t *s) {
/* Add this connection to our array */
web_sockets[num_web_sockets++] = s;
/* We could wait with this until properly upgraded */
if (--connections) {
us_socket_context_connect(SSL, us_socket_context(SSL, s), host, port, NULL, 0, sizeof(struct http_socket));
} else {
printf("Running benchmark now...\n");
start_iteration();
us_socket_timeout(SSL, s, LIBUS_TIMEOUT_GRANULARITY);
}
}
struct us_socket_t *on_http_socket_writable(struct us_socket_t *s) {
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
/* Are we still not upgraded yet? */
if (http_socket->upgrade_offset < sizeof(request) - 1) {
http_socket->upgrade_offset += us_socket_write(SSL, s, request + http_socket->upgrade_offset, sizeof(request) - 1 - http_socket->upgrade_offset, 0);
} else {
/* Stream whatever is remaining of the request */
http_socket->offset += us_socket_write(SSL, s, (char *) web_socket_request + http_socket->offset, sizeof(web_socket_request) - http_socket->offset, 0);
}
return s;
}
struct us_socket_t *on_http_socket_close(struct us_socket_t *s, int code, void *reason) {
printf("Client was disconnected, exiting!\n");
exit(-1);
return s;
}
struct us_socket_t *on_http_socket_end(struct us_socket_t *s) {
return us_socket_close(SSL, s, 0, NULL);
}
struct us_socket_t *on_http_socket_data(struct us_socket_t *s, char *data, int length) {
/* Get socket extension and the socket's context's extension */
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
/* Are we already upgraded? */
if (http_socket->is_upgraded) {
http_socket->bytes_received += length;
if (http_socket->bytes_received == (sizeof(web_socket_request) - 4) * num_web_sockets) {
satisfied_sockets++;
http_socket->bytes_received = 0;
if (satisfied_sockets == num_web_sockets) {
iterations++;
satisfied_sockets = 0;
start_iteration();
}
}
} else {
/* We assume the server is not sending anything immediately following upgrade and that we get rnrn in one chunk */
if (length >= 4 && data[length - 1] == '\n' && data[length - 2] == '\r' && data[length - 3] == '\n' && data[length - 4] == '\r') {
http_socket->is_upgraded = 1;
next_connection(s);
}
}
return s;
}
struct us_socket_t *on_http_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) {
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
/* Reset offsets */
http_socket->offset = 0;
http_socket->is_upgraded = 0;
http_socket->bytes_received = 0;
/* Send an upgrade request */
http_socket->upgrade_offset = us_socket_write(SSL, s, request, sizeof(request) - 1, 0);
return s;
}
struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) {
/* Print current statistics */
printf("Iterations/second (%d clients): %f\n", num_web_sockets, ((float)iterations) / LIBUS_TIMEOUT_GRANULARITY);
iterations = 0;
us_socket_timeout(SSL, s, LIBUS_TIMEOUT_GRANULARITY);
return s;
}
int main(int argc, char **argv) {
/* Parse host and port */
if (argc != 5) {
printf("Usage: connections host port ssl\n");
return 0;
}
port = atoi(argv[3]);
host = malloc(strlen(argv[2]) + 1);
memcpy(host, argv[2], strlen(argv[2]) + 1);
connections = atoi(argv[1]);
SSL = atoi(argv[4]);
/* Allocate room for every socket */
web_sockets = (void **) malloc(sizeof(void *) * connections);
/* Create the event loop */
struct us_loop_t *loop = us_create_loop(0, noop, noop, noop, 0);
/* Create a socket context for HTTP */
struct us_socket_context_options_t options = {};
struct us_socket_context_t *http_context = us_create_socket_context(SSL, loop, 0, options);
/* Set up event handlers */
us_socket_context_on_open(SSL, http_context, on_http_socket_open);
us_socket_context_on_data(SSL, http_context, on_http_socket_data);
us_socket_context_on_writable(SSL, http_context, on_http_socket_writable);
us_socket_context_on_close(SSL, http_context, on_http_socket_close);
us_socket_context_on_timeout(SSL, http_context, on_http_socket_timeout);
us_socket_context_on_end(SSL, http_context, on_http_socket_end);
/* Start making HTTP connections */
us_socket_context_connect(SSL, http_context, host, port, NULL, 0, sizeof(struct http_socket));
us_loop_run(loop);
}
================================================
FILE: benchmarks/load_test.c
================================================
#define _DEFAULT_SOURCE
#ifdef __APPLE__
#include
#define htobe16(x) OSSwapHostToBigInt16(x)
#define htole16(x) OSSwapHostToLittleInt16(x)
#define be16toh(x) OSSwapBigToHostInt16(x)
#define le16toh(x) OSSwapLittleToHostInt16(x)
#define htobe32(x) OSSwapHostToBigInt32(x)
#define htole32(x) OSSwapHostToLittleInt32(x)
#define be32toh(x) OSSwapBigToHostInt32(x)
#define le32toh(x) OSSwapLittleToHostInt32(x)
#define htobe64(x) OSSwapHostToBigInt64(x)
#define htole64(x) OSSwapHostToLittleInt64(x)
#define be64toh(x) OSSwapBigToHostInt64(x)
#define le64toh(x) OSSwapLittleToHostInt64(x)
#else
#include
#endif
#include
#include
#include
#include
#include
#include
#include
/** @brief Structure to hold per-socket state */
typedef struct {
int offset; // Bytes sent of the WebSocket request
int upgrade_offset; // Bytes sent of the upgrade request
int is_upgraded; // Whether the socket has been upgraded to WebSocket
int outstanding_bytes; // Expected bytes to receive for echo completion
} SocketState;
/** @brief Structure to hold benchmark configuration */
typedef struct {
char *host; // Target server host
int port; // Target server port
int SSL; // Whether to use SSL (0 or 1)
int deflate; // Whether to use compression (0 or 1)
int payload_size; // Size of the payload in bytes
unsigned char *web_socket_request; // Precomputed WebSocket request frame
int web_socket_request_size; // Size of the request frame
int web_socket_request_response_size;// Expected response size
char *upgrade_request; // HTTP upgrade request string
int upgrade_request_length; // Length of the upgrade request
} BenchmarkConfig;
// Static variables for configuration and shared state
static BenchmarkConfig config;
static int connections; // Number of remaining connections to establish
static int responses = 0;// Number of responses received
// Predefined static data
static char request_deflate[] = "GET / HTTP/1.1\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\nSec-WebSocket-Extensions: permessage-deflate\r\nHost: server.example.com\r\nSec-WebSocket-Version: 13\r\n\r\n";
static char request_text[] = "GET / HTTP/1.1\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\nHost: server.example.com\r\nSec-WebSocket-Version: 13\r\n\r\n";
/**
* @brief Initializes a text WebSocket request
* @param size Payload size in bytes
* @param config Pointer to the BenchmarkConfig to populate
*/
void init_text_request(int size, BenchmarkConfig *config) {
if (size <= 125) {
config->web_socket_request_size = size + 6;
config->web_socket_request = malloc(config->web_socket_request_size);
config->web_socket_request[0] = 130; // FIN + opcode 1 (text)
config->web_socket_request[1] = 128 | size; // Mask bit + length
config->web_socket_request[2] = 1;
config->web_socket_request[3] = 2;
config->web_socket_request[4] = 3;
config->web_socket_request[5] = 4;
memset(config->web_socket_request + 6, 'T', size);
} else if (size <= 65535) {
config->web_socket_request_size = size + 8;
config->web_socket_request = malloc(config->web_socket_request_size);
config->web_socket_request[0] = 130;
config->web_socket_request[1] = 128 | 126;
uint16_t len = htobe16(size);
memcpy(&config->web_socket_request[2], &len, 2);
config->web_socket_request[4] = 1;
config->web_socket_request[5] = 2;
config->web_socket_request[6] = 3;
config->web_socket_request[7] = 4;
memset(config->web_socket_request + 8, 'T', size);
} else {
config->web_socket_request_size = size + 14;
config->web_socket_request = malloc(config->web_socket_request_size);
config->web_socket_request[0] = 130;
config->web_socket_request[1] = 128 | 127;
uint64_t len = htobe64(size);
memcpy(&config->web_socket_request[2], &len, 8);
config->web_socket_request[10] = 1;
config->web_socket_request[11] = 2;
config->web_socket_request[12] = 3;
config->web_socket_request[13] = 4;
memset(config->web_socket_request + 14, 'T', size);
}
config->web_socket_request_response_size = config->web_socket_request_size;
}
/**
* @brief Initializes a deflated WebSocket request
* @param size Size of the uncompressed payload in bytes
* @param config Pointer to the BenchmarkConfig to populate
*/
void init_deflated_request(int size, BenchmarkConfig *config) {
const char placeholder[] = "{\"userId\":12345,\"action\":\"purchase\",\"items\":[{\"id\":\"A1B2C3\",\"name\":\"Wireless Mouse\",\"price\":25.99,\"quantity\":1},{\"id\":\"D4E5F6\",\"name\":\"Mechanical Keyboard\",\"price\":89.99,\"quantity\":1}],\"payment\":{\"method\":\"credit_card\",\"transactionId\":\"XYZ987654321\",\"status\":\"approved\"},\"timestamp\":\"2025-02-20T15:30:00Z\"};";
char *json_message = malloc(size);
int placeholder_len = sizeof(placeholder) - 1;
printf("Using placeholder of %d bytes\n", placeholder_len);
for (int i = 0; i < size; i += placeholder_len) {
int copy_len = (i + placeholder_len <= size) ? placeholder_len : size - i;
memcpy(json_message + i, placeholder, copy_len);
}
z_stream defstream;
defstream.zalloc = Z_NULL;
defstream.zfree = Z_NULL;
defstream.opaque = Z_NULL;
deflateInit2(&defstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
uLongf compressed_max_size = deflateBound(&defstream, (uLong)size);
Bytef *compressed_data = malloc(compressed_max_size);
defstream.avail_in = (uInt)size;
defstream.next_in = (Bytef *)json_message;
defstream.avail_out = (uInt)compressed_max_size;
defstream.next_out = compressed_data;
int res = deflate(&defstream, Z_SYNC_FLUSH);
if (res != Z_OK) {
printf("Deflation failed: %d\n", res);
exit(1);
}
uLongf compressed_size = defstream.total_out - 4;
deflateEnd(&defstream);
free(json_message);
unsigned char mask[4] = {0, 0, 0, 0};
for (uLongf i = 0; i < compressed_size; i++) {
compressed_data[i] ^= mask[i % 4];
}
unsigned char header[14];
int header_len;
if (compressed_size <= 125) {
header_len = 6;
header[0] = 0xC2; // FIN + RSV1 + Opcode 2
header[1] = 0x80 | compressed_size;
memcpy(header + 2, mask, 4);
} else if (compressed_size <= 65535) {
header_len = 8;
header[0] = 0xC2;
header[1] = 0x80 | 126;
header[2] = (compressed_size >> 8) & 0xFF;
header[3] = compressed_size & 0xFF;
memcpy(header + 4, mask, 4);
} else {
header_len = 14;
header[0] = 0xC2;
header[1] = 0x80 | 127;
for (int i = 0; i < 8; i++) {
header[2 + i] = (compressed_size >> (56 - i * 8)) & 0xFF;
}
memcpy(header + 10, mask, 4);
}
config->web_socket_request_size = header_len + compressed_size;
config->web_socket_request = malloc(config->web_socket_request_size);
memcpy(config->web_socket_request, header, header_len);
memcpy(config->web_socket_request + header_len, compressed_data, compressed_size);
free(compressed_data);
int server_will_compress = 0;
if (server_will_compress) {
config->web_socket_request_response_size = config->web_socket_request_size;
} else {
if (size <= 125) {
config->web_socket_request_response_size = 6 + size;
} else if (size <= 65535) {
config->web_socket_request_response_size = 8 + size;
} else {
config->web_socket_request_response_size = 14 + size;
}
}
}
/**
* @brief Initiates the next connection or starts the benchmark
* @param s Current socket
*/
void next_connection(struct us_socket_t *s) {
if (--connections) {
us_socket_context_connect(config.SSL, us_socket_context(config.SSL, s), config.host, config.port, NULL, 0, sizeof(SocketState));
} else {
printf("Running benchmark now...\n");
us_socket_timeout(config.SSL, s, LIBUS_TIMEOUT_GRANULARITY);
}
}
/** @brief Handler for when a socket becomes writable */
struct us_socket_t *on_http_socket_writable(struct us_socket_t *s) {
SocketState *state = (SocketState *)us_socket_ext(config.SSL, s);
if (state->upgrade_offset < config.upgrade_request_length) {
state->upgrade_offset += us_socket_write(config.SSL, s, config.upgrade_request + state->upgrade_offset, config.upgrade_request_length - state->upgrade_offset, 0);
if (state->upgrade_offset == config.upgrade_request_length) {
next_connection(s);
}
} else {
state->offset += us_socket_write(config.SSL, s, (char *)config.web_socket_request + state->offset, config.web_socket_request_size - state->offset, 0);
}
return s;
}
/** @brief Handler for socket closure */
struct us_socket_t *on_http_socket_close(struct us_socket_t *s, int code, void *reason) {
printf("Closed!\n");
return s;
}
/** @brief Handler for socket end */
struct us_socket_t *on_http_socket_end(struct us_socket_t *s) {
return us_socket_close(config.SSL, s, 0, NULL);
}
/** @brief Handler for incoming data */
struct us_socket_t *on_http_socket_data(struct us_socket_t *s, char *data, int length) {
SocketState *state = (SocketState *)us_socket_ext(config.SSL, s);
if (state->is_upgraded) {
state->outstanding_bytes -= length;
if (state->outstanding_bytes == 0) {
state->offset = us_socket_write(config.SSL, s, (char *)config.web_socket_request, config.web_socket_request_size, 0);
state->outstanding_bytes = config.web_socket_request_response_size - 4;
responses++;
} else if (state->outstanding_bytes < 0) {
printf("ERROR: outstanding bytes negative!\n");
exit(0);
}
} else {
if (length >= 4 && memcmp(data + length - 4, "\r\n\r\n", 4) == 0) {
printf("Response: %.*s\n", length, data);
state->offset = us_socket_write(config.SSL, s, (char *)config.web_socket_request, config.web_socket_request_size, 0);
state->outstanding_bytes = config.web_socket_request_response_size - 4;
state->is_upgraded = 1;
}
}
return s;
}
/** @brief Handler for socket opening */
struct us_socket_t *on_http_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) {
SocketState *state = (SocketState *)us_socket_ext(config.SSL, s);
state->offset = 0;
state->is_upgraded = 0;
state->upgrade_offset = us_socket_write(config.SSL, s, config.upgrade_request, config.upgrade_request_length, 0);
if (state->upgrade_offset == config.upgrade_request_length) {
next_connection(s);
}
return s;
}
/** @brief Handler for socket timeout */
struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) {
printf("Msg/sec: %f\n", ((float)responses) / LIBUS_TIMEOUT_GRANULARITY);
responses = 0;
us_socket_timeout(config.SSL, s, LIBUS_TIMEOUT_GRANULARITY);
return s;
}
// Empty loop callbacks required by libusockets
void on_wakeup(struct us_loop_t *loop) {}
void on_pre(struct us_loop_t *loop) {}
void on_post(struct us_loop_t *loop) {}
/**
* @brief Main entry point for the WebSocket benchmark
* @param argc Number of command-line arguments
* @param argv Array of command-line arguments
* @return Exit status
*/
int main(int argc, char **argv) {
if (argc != 6 && argc != 7) {
printf("Usage: connections host port ssl deflate [payload_size_bytes]\n");
return 0;
}
// Initialize configuration
config.host = malloc(strlen(argv[2]) + 1);
memcpy(config.host, argv[2], strlen(argv[2]) + 1);
config.port = atoi(argv[3]);
connections = atoi(argv[1]);
config.SSL = atoi(argv[4]);
config.deflate = atoi(argv[5]);
// Set up upgrade request and WebSocket request based on deflate option
if (config.deflate) {
config.upgrade_request = request_deflate;
config.upgrade_request_length = sizeof(request_deflate) - 1;
config.payload_size = (argc == 7) ? atoi(argv[6]) : 5;
init_deflated_request(config.payload_size, &config);
printf("Using message size of %d bytes compressed down to %d bytes\n", config.payload_size, config.web_socket_request_size);
} else {
config.upgrade_request = request_text;
config.upgrade_request_length = sizeof(request_text) - 1;
if (argc == 7) {
config.payload_size = atoi(argv[6]);
} else {
config.payload_size = 20;
}
init_text_request(config.payload_size, &config);
printf("Using message size of %d bytes\n", config.payload_size);
}
// Create and run the event loop
struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0);
struct us_socket_context_options_t options = {0};
struct us_socket_context_t *http_context = us_create_socket_context(config.SSL, loop, 0, options);
// Register event handlers
us_socket_context_on_open(config.SSL, http_context, on_http_socket_open);
us_socket_context_on_data(config.SSL, http_context, on_http_socket_data);
us_socket_context_on_writable(config.SSL, http_context, on_http_socket_writable);
us_socket_context_on_close(config.SSL, http_context, on_http_socket_close);
us_socket_context_on_timeout(config.SSL, http_context, on_http_socket_timeout);
us_socket_context_on_end(config.SSL, http_context, on_http_socket_end);
// Start the first connection
us_socket_context_connect(config.SSL, http_context, config.host, config.port, NULL, 0, sizeof(SocketState));
us_loop_run(loop);
// Cleanup
free(config.host);
free(config.web_socket_request);
us_socket_context_free(config.SSL, http_context);
us_loop_free(loop);
return 0;
}
================================================
FILE: benchmarks/parser.cpp
================================================
/* This is a fuzz test of the websocket parser */
#define WIN32_EXPORT
/* We test the websocket parser */
#include "../src/WebSocketProtocol.h"
unsigned int messages = 0;
struct Impl {
static bool refusePayloadLength(uint64_t length, uWS::WebSocketState *wState, void *s) {
/* We need a limit */
if (length > 16000) {
return true;
}
/* Return ok */
return false;
}
static bool setCompressed(uWS::WebSocketState *wState, void *s) {
/* We support it */
return true;
}
static void forceClose(uWS::WebSocketState *wState, void *s, std::string_view reason = {}) {
}
static bool handleFragment(char *data, size_t length, unsigned int remainingBytes, int opCode, bool fin, uWS::WebSocketState *webSocketState, void *s) {
if (opCode == uWS::TEXT) {
if (!uWS::protocol::isValidUtf8((unsigned char *)data, length)) {
/* Return break */
return true;
}
} else if (opCode == uWS::CLOSE) {
uWS::protocol::parseClosePayload((char *)data, length);
}
messages += 1;
/* Return ok */
return false;
}
};
#include
int web_socket_request_text_size;
char *web_socket_request_text;
void init_medium_message(unsigned int size) {
if (size > 65536) {
printf("Error: message size must be smaller\n");
exit(0);
}
web_socket_request_text_size = size + 6 + 2; // 8 for big
web_socket_request_text = ((char *) malloc(32 + web_socket_request_text_size + 32)) + 32;
memset(web_socket_request_text, 'T', web_socket_request_text_size + 32);
web_socket_request_text[0] = 130;
web_socket_request_text[1] = 254;
uint16_t msg_size = htobe16(size);
memcpy(&web_socket_request_text[2], &msg_size, 2);
web_socket_request_text[4] = 1;
web_socket_request_text[5] = 2;
web_socket_request_text[6] = 3;
web_socket_request_text[7] = 4;
}
int main() {
init_medium_message(1024);
/* Create the parser state */
uWS::WebSocketState state;
unsigned char pre[32];
unsigned char web_socket_request_text_small[26] = {130, 128 | 20, 1, 2, 3, 4};
unsigned char post[32];
uint16_t msg_size = htobe16(1024);
{
clock_t start = clock();
for (unsigned long long i = 0; i < 100000000; i++) {
web_socket_request_text[0] = 130;
web_socket_request_text[1] = 254;
memcpy(&web_socket_request_text[2], &msg_size, 2);
web_socket_request_text[4] = 1;
web_socket_request_text[5] = 2;
web_socket_request_text[6] = 3;
web_socket_request_text[7] = 4;
// here we can either consume the whole message or consume the whole message minus 1 byte, causing a different path to be taken
uWS::WebSocketProtocol::consume((char *) web_socket_request_text, web_socket_request_text_size-1, &state, nullptr);
}
clock_t stop = clock();
float seconds = ((float)(stop-start)/CLOCKS_PER_SEC);
std::cout << std::fixed << "Parsed incomplete 1 kB messages per second: " << ((float)messages / seconds) << std::endl;
}
{
messages = 0;
clock_t start = clock();
for (unsigned long long i = 0; i < 100000000; i++) {
web_socket_request_text[0] = 130;
web_socket_request_text[1] = 254;
memcpy(&web_socket_request_text[2], &msg_size, 2);
web_socket_request_text[4] = 1;
web_socket_request_text[5] = 2;
web_socket_request_text[6] = 3;
web_socket_request_text[7] = 4;
// here we can either consume the whole message or consume the whole message minus 1 byte, causing a different path to be taken
uWS::WebSocketProtocol::consume((char *) web_socket_request_text, web_socket_request_text_size, &state, nullptr);
}
clock_t stop = clock();
float seconds = ((float)(stop-start)/CLOCKS_PER_SEC);
std::cout << std::fixed << "Parsed complete 1 kB messages per second: " << ((float)messages / seconds) << std::endl;
}
return 0;
}
================================================
FILE: benchmarks/scale_test.c
================================================
/* This is a scalability test for testing million(s) of pinging connections */
#include
int SSL;
#include
#include
#include
#include
#include
unsigned char web_socket_request[26] = {130, 128 | 20, 1, 2, 3, 4};
char request[] = "GET / HTTP/1.1\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"
"Host: server.example.com\r\n"
"Sec-WebSocket-Version: 13\r\n\r\n";
char *host;
int port;
int connections;
/* All the ips we as client can use */
char **ips;
int num_ips;
/* Send ping every 16 seconds */
int WEBSOCKET_PING_INTERVAL = 8;
/* We only establish 20k connections per address */
int CONNECTIONS_PER_ADDRESS = 20000;
/* How many connections a time */
int BATCH_CONNECT = 1;
/* Currently open and alive connections */
int opened_connections;
/* Dead connections */
int closed_connections;
struct http_socket {
/* How far we have streamed our websocket request */
int offset;
/* How far we have streamed our upgrade request */
int upgrade_offset;
};
struct us_socket_t *next_connection_failed = 0;
/* We don't need any of these */
void on_pre(struct us_loop_t *loop) {
}
/* This is not HTTP POST, it is merely an event emitted post loop iteration */
void on_post(struct us_loop_t *loop) {
}
void next_connection(struct us_socket_t *s) {
/* We could wait with this until properly upgraded */
if (--connections/* > BATCH_CONNECT*/) {
/* Swap address */
int address = opened_connections / CONNECTIONS_PER_ADDRESS;
if (us_socket_context_connect(SSL, us_socket_context(SSL, s), host, port, ips[address], 0, sizeof(struct http_socket)) == 0) {
printf("Next connection failed immediately\n");
/* Try agsin next event loop iteration */
next_connection_failed = s;
us_wakeup_loop(us_socket_context_loop(0, us_socket_context(0, s)));
}
}
}
void on_wakeup(struct us_loop_t *loop) {
if (next_connection_failed) {
struct us_socket_t *s = next_connection_failed;
next_connection_failed = 0;
next_connection(s);
}
}
struct us_socket_t *on_http_socket_writable(struct us_socket_t *s) {
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
/* Are we still not upgraded yet? */
if (http_socket->upgrade_offset < sizeof(request) - 1) {
http_socket->upgrade_offset += us_socket_write(SSL, s, request + http_socket->upgrade_offset, sizeof(request) - 1 - http_socket->upgrade_offset, 0);
/* Now we should be */
if (http_socket->upgrade_offset == sizeof(request) - 1) {
next_connection(s);
/* Make sure to send ping */
us_socket_timeout(SSL, s, WEBSOCKET_PING_INTERVAL);
}
} else {
/* Stream whatever is remaining of the request */
http_socket->offset += us_socket_write(SSL, s, (char *) web_socket_request + http_socket->offset, sizeof(web_socket_request) - http_socket->offset, 0);
if (http_socket->offset == sizeof(web_socket_request)) {
/* Reset timeout if we managed to */
us_socket_timeout(SSL, s, WEBSOCKET_PING_INTERVAL);
}
}
return s;
}
struct us_socket_t *on_http_socket_close(struct us_socket_t *s, int code, void *reason) {
closed_connections++;
if (closed_connections % 1000 == 0) {
printf("Alive: %d, dead: %d\n", opened_connections, closed_connections);
}
return s;
}
struct us_socket_t *on_http_socket_end(struct us_socket_t *s) {
return us_socket_close(SSL, s, 0, NULL);
}
// should never get a response!
struct us_socket_t *on_http_socket_data(struct us_socket_t *s, char *data, int length) {
// is this a broadcasted unix time in millis?
if (length % 10 == 0) {
// data sent first will come first, so it is oldest
struct timespec ts;
timespec_get(&ts, TIME_UTC);
int64_t millis = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
int64_t received_millis;
memcpy(&received_millis, data + 2, 8);
static int counter;
static int max_latency;
static long average_latency;
int latency = millis - received_millis;
average_latency += latency;
if (max_latency < latency) {
max_latency = latency;
}
if (++counter % 10000 == 0) {
printf("Alive: %d, dead: %d\n", opened_connections, closed_connections);
printf("Max latency: %d ms\n", max_latency);
printf("Average latency: %ld ms\n\n", average_latency / 10000);
max_latency = 0;
average_latency = 0;
}
}
return s;
}
struct us_socket_t *on_http_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) {
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
/* Display number of opened connections */
opened_connections++;
if (opened_connections % 1000 == 0) {
printf("Alive: %d, dead: %d\n", opened_connections, closed_connections);
}
/* Send an upgrade request */
http_socket->upgrade_offset = us_socket_write(SSL, s, request, sizeof(request) - 1, 0);
if (http_socket->upgrade_offset == sizeof(request) - 1) {
next_connection(s);
/* Make sure to send ping */
us_socket_timeout(SSL, s, WEBSOCKET_PING_INTERVAL);
}
return s;
}
// here we should send a message as ping (part of the test)
struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) {
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
/* Send ping here */
http_socket->offset = us_socket_write(SSL, s, (char *) web_socket_request, sizeof(web_socket_request), 0);
if (http_socket->offset == sizeof(web_socket_request)) {
/* Reset timeout if we managed to */
us_socket_timeout(SSL, s, WEBSOCKET_PING_INTERVAL);
}
return s;
}
struct us_socket_t *on_http_socket_connect_error(struct us_socket_t *s, int code) {
printf("Connection failed\n");
next_connection(s);
return s;
}
int main(int argc, char **argv) {
/* Parse host and port */
if (argc < 5) {
printf("Usage: connections host port ssl [ip ...]\n");
return 0;
}
port = atoi(argv[3]);
host = malloc(strlen(argv[2]) + 1);
memcpy(host, argv[2], strlen(argv[2]) + 1);
connections = atoi(argv[1]);
SSL = atoi(argv[4]);
/* Do we have ip addresses? */
if (argc > 5) {
ips = &argv[5];
num_ips = argc - 5;
for (int i = 0; i < num_ips; i++) {
printf("%s\n", ips[i]);
}
} else {
static char *default_ips[] = {""};
ips = default_ips;
num_ips = 1;
}
/* Check so that we have enough ip addresses */
if (num_ips <= connections / CONNECTIONS_PER_ADDRESS) {
printf("You'll need more IP addresses for this run\n");
return 0;
}
/* Create the event loop */
struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0);
/* Create a socket context for HTTP */
struct us_socket_context_options_t options = {};
struct us_socket_context_t *http_context = us_create_socket_context(SSL, loop, 0, options);
/* Set up event handlers */
us_socket_context_on_open(SSL, http_context, on_http_socket_open);
us_socket_context_on_data(SSL, http_context, on_http_socket_data);
us_socket_context_on_writable(SSL, http_context, on_http_socket_writable);
us_socket_context_on_close(SSL, http_context, on_http_socket_close);
us_socket_context_on_timeout(SSL, http_context, on_http_socket_timeout);
us_socket_context_on_end(SSL, http_context, on_http_socket_end);
us_socket_context_on_connect_error(SSL, http_context, on_http_socket_connect_error);
/* Start making HTTP connections */
for (int i = 0; i < BATCH_CONNECT; i++) {
if (us_socket_context_connect(SSL, http_context, host, port, ips[0], 0, sizeof(struct http_socket)) == 0) {
printf("Connection failed immediately\n");
return 0;
}
}
us_loop_run(loop);
}
================================================
FILE: build.c
================================================
#include "build.h"
int main(int argc, char **argv) {
/* Some variables we need */
char *CXXFLAGS = strncpy(calloc(1024, 1), maybe(getenv("CXXFLAGS")), 1024);
char *CFLAGS = strncpy(calloc(1024, 1), maybe(getenv("CFLAGS")), 1024);
char *LDFLAGS = strncpy(calloc(1024, 1), maybe(getenv("LDFLAGS")), 1024);
char *CC = strncpy(calloc(1024, 1), or_else(getenv("CC"), "cc"), 1024);
char *CXX = strncpy(calloc(1024, 1), or_else(getenv("CXX"), "g++"), 1024);
char *EXEC_SUFFIX = strncpy(calloc(1024, 1), maybe(getenv("EXEC_SUFFIX")), 1024);
char *EXAMPLE_FILES[] = {"SecureGzipFileServer", "Precompress", "EchoBody", "HelloWorldThreaded", "Http3Server", "Broadcast", "HelloWorld", "Crc32", "ServerName",
"EchoServer", "BroadcastingEchoServer", "UpgradeSync", "UpgradeAsync", "ParameterRoutes"};
strcat(CXXFLAGS, " -march=native -O3 -Wpedantic -Wall -Wextra -Wsign-conversion -Wconversion -std=c++2b -Isrc -IuSockets/src");
strcat(LDFLAGS, " uSockets/*.o");
// We can use libdeflate as a fast path to zlib (you need to build it first)
if (env_is("WITH_LIBDEFLATE", "1")) {
strcat(LDFLAGS, " libdeflate/libdeflate.a");
strcat(CXXFLAGS, " -DUWS_USE_LIBDEFLATE -I libdeflate");
}
// By default we use LTO, but Windows does not support it
if (!env_is("WITH_LTO", "0")) {
strcat(CXXFLAGS, " -flto=auto");
}
// By default we use zlib but you can build without it (disables permessage-deflate)
if (!env_is("WITH_ZLIB", "0")) {
strcat(LDFLAGS, " -lz");
} else {
strcat(CXXFLAGS, " -DUWS_NO_ZLIB");
}
// WITH_PROXY enables PROXY Protocol v2 support
if (env_is("WITH_PROXY", "1")) {
strcat(CXXFLAGS, " -DUWS_WITH_PROXY");
}
// WITH_QUIC enables experimental Http3 examples
if (env_is("WITH_QUIC", "1")) {
strcat(CXXFLAGS, " -DLIBUS_USE_QUIC");
strcat(LDFLAGS, " -pthread -lz -lm uSockets/lsquic/src/liblsquic/liblsquic.a");
}
// Heavily prefer boringssl over openssl
if (env_is("WITH_BORINGSSL", "1")) {
strcat(CFLAGS, " -I uSockets/boringssl/include -pthread -DLIBUS_USE_OPENSSL");
strcat(LDFLAGS, " -pthread uSockets/boringssl/build/ssl/libssl.a uSockets/boringssl/build/crypto/libcrypto.a");
} else {
// WITH_OPENSSL=1 enables OpenSSL 1.1+ support
if (env_is("WITH_OPENSSL", "1")) {
// With problems on macOS, make sure to pass needed LDFLAGS required to find these
strcat(LDFLAGS, " -lssl -lcrypto");
} else {
// WITH_WOLFSSL=1 enables WolfSSL 4.2.0 support (mutually exclusive with OpenSSL)
if (env_is("WITH_WOLFSSL", "1")) {
strcat(LDFLAGS, " -L/usr/local/lib -lwolfssl");
}
}
}
// WITH_LIBUV=1 builds with libuv as event-loop
if (env_is("WITH_LIBUV", "1")) {
strcat(LDFLAGS, " -luv");
}
// WITH_ASIO=1 builds with ASIO as event-loop
if (env_is("WITH_ASIO", "1")) {
strcat(CXXFLAGS, " -pthread");
strcat(LDFLAGS, " -lpthread");
}
// WITH_ASAN builds with sanitizers
if (env_is("WITH_ASAN", "1")) {
strcat(CXXFLAGS, " -fsanitize=address -g");
strcat(LDFLAGS, " -lasan");
}
if (!strcmp(argv[1], "examples")) {
#pragma omp parallel for
for (int i = 0; i < sizeof(EXAMPLE_FILES) / sizeof(char *); i++) {
if (run("%s %s examples/%s.cpp %s -o %s%s", CXX, CXXFLAGS, EXAMPLE_FILES[i], LDFLAGS, EXAMPLE_FILES[i], EXEC_SUFFIX)) {
exit(-1);
}
}
} else if (!strcmp(argv[1], "capi")) {
printf("capi target does nothing yet\n");
} else if (!strcmp(argv[1], "clean")) {
printf("clean target does nothing yet\n");
} else if (!strcmp(argv[1], "install")) {
// install target is not even supposed to be cross platform
printf("install target does nothing yet\n");
} else if (!strcmp(argv[1], "all")) {
printf("all target does nothing yet\n");
}
}
================================================
FILE: build.h
================================================
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
int env_is(char *env, char *target) {
char *val = getenv(env);
return val && !strcmp(val, target);
}
char *maybe(char *in) {
return in ? in : "";
}
char *or_else(char *in, char *otherwise) {
return in ? in : otherwise;
}
int run(const char *cmd, ...) {
char buf[2048];
va_list args;
va_start(args, cmd);
vsprintf(buf, cmd, args);
va_end(args);
printf("--> %s\n\n", buf);
return system(buf);
}
================================================
FILE: cluster/README.md
================================================
# uWS Cluster
================================================
FILE: examples/Broadcast.cpp
================================================
/* We simply call the root header file "App.h", giving you uWS::App and uWS::SSLApp */
#include "App.h"
#include
#include
/* This is a simple WebSocket echo server example.
* You may compile it with "WITH_OPENSSL=1 make" or with "make" */
uWS::SSLApp *globalApp;
int main() {
/* ws->getUserData returns one of these */
struct PerSocketData {
/* Fill with user data */
};
/* Keep in mind that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support.
* You may swap to using uWS:App() if you don't need SSL */
uWS::SSLApp app = uWS::SSLApp({
/* There are example certificates in uWebSockets.js repo */
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
}).ws("/*", {
/* Settings */
.compression = uWS::SHARED_COMPRESSOR,
.maxPayloadLength = 16 * 1024 * 1024,
.idleTimeout = 16,
.maxBackpressure = 1 * 1024 * 1024,
.closeOnBackpressureLimit = false,
.resetIdleTimeoutOnSend = false,
.sendPingsAutomatically = true,
/* Handlers */
.upgrade = nullptr,
.open = [](auto *ws) {
/* Open event here, you may access ws->getUserData() which points to a PerSocketData struct */
ws->subscribe("broadcast");
},
.message = [](auto */*ws*/, std::string_view /*message*/, uWS::OpCode /*opCode*/) {
},
.drain = [](auto */*ws*/) {
/* Check ws->getBufferedAmount() here */
},
.ping = [](auto */*ws*/, std::string_view) {
/* Not implemented yet */
},
.pong = [](auto */*ws*/, std::string_view) {
/* Not implemented yet */
},
.close = [](auto */*ws*/, int /*code*/, std::string_view /*message*/) {
/* You may access ws->getUserData() here */
}
}).listen(9001, [](auto *listen_socket) {
if (listen_socket) {
std::cout << "Listening on port " << 9001 << std::endl;
}
});
struct us_loop_t *loop = (struct us_loop_t *) uWS::Loop::get();
struct us_timer_t *delayTimer = us_create_timer(loop, 0, 0);
// broadcast the unix time as millis every 8 millis
us_timer_set(delayTimer, [](struct us_timer_t */*t*/) {
struct timespec ts;
timespec_get(&ts, TIME_UTC);
int64_t millis = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
//std::cout << "Broadcasting timestamp: " << millis << std::endl;
globalApp->publish("broadcast", std::string_view((char *) &millis, sizeof(millis)), uWS::OpCode::BINARY, false);
}, 8, 8);
globalApp = &app;
app.run();
}
================================================
FILE: examples/BroadcastingEchoServer.cpp
================================================
#include "App.h"
struct us_listen_socket_t *global_listen_socket;
int main() {
/* ws->getUserData returns one of these */
struct PerSocketData {
/* Fill with user data */
std::vector topics;
int nr = 0;
};
/* Keep in mind that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support.
* You may swap to using uWS:App() if you don't need SSL */
uWS::SSLApp *app = new uWS::SSLApp({
/* There are example certificates in uWebSockets.js repo */
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
});
app->ws("/*", {
/* Settings */
.compression = uWS::DISABLED,
.maxPayloadLength = 16 * 1024 * 1024,
.idleTimeout = 60,
.maxBackpressure = 16 * 1024 * 1024,
.closeOnBackpressureLimit = false,
.resetIdleTimeoutOnSend = true,
.sendPingsAutomatically = false,
/* Handlers */
.upgrade = nullptr,
.open = [](auto *ws) {
/* Open event here, you may access ws->getUserData() which points to a PerSocketData struct */
PerSocketData *perSocketData = (PerSocketData *) ws->getUserData();
for (int i = 0; i < 32; i++) {
std::string topic = std::to_string((uintptr_t)ws) + "-" + std::to_string(i);
perSocketData->topics.push_back(topic);
ws->subscribe(topic);
}
},
.message = [&app](auto *ws, std::string_view message, uWS::OpCode opCode) {
PerSocketData *perSocketData = (PerSocketData *) ws->getUserData();
app->publish(perSocketData->topics[(size_t)(++perSocketData->nr % 32)], message, opCode);
ws->publish(perSocketData->topics[(size_t)(++perSocketData->nr % 32)], message, opCode);
},
.drain = [](auto */*ws*/) {
/* Check ws->getBufferedAmount() here */
//std::cout << "drain" << std::endl;
},
.ping = [](auto */*ws*/, std::string_view ) {
/* Not implemented yet */
},
.pong = [](auto */*ws*/, std::string_view ) {
/* Not implemented yet */
},
.close = [](auto */*ws*/, int /*code*/, std::string_view /*message*/) {
/* You may access ws->getUserData() here */
}
}).listen(9001, [](auto *listen_s) {
if (listen_s) {
std::cout << "Listening on port " << 9001 << std::endl;
//listen_socket = listen_s;
}
});
app->run();
delete app;
uWS::Loop::get()->free();
}
================================================
FILE: examples/CachingApp.cpp
================================================
#include "App.h"
#include
int main() {
uWS::App app;
/* Regular, non-cached response */
app.get("/not-cached", [](auto *res, auto */*req*/) {
res->end("Responding without a cache");
}).get("/*", [](auto *res, auto */*req*/) {
/* A cached response with 5 seconds of lifetime */
std::cout << "Filling cache now" << std::endl;
res->end("This is a response");
}, 5).listen(8080, [](bool success) {
if (success) {
std::cout << "Listening on port 8080" << std::endl;
} else {
std::cerr << "Failed to listen on port 8080" << std::endl;
}
});
app.run();
}
================================================
FILE: examples/Client.cpp
================================================
// This example is broken and doesn't do anything. It is a potential interface for a future potential client.
// There is no client support implemented in the library. This example is a placeholder.
#include "ClientApp.h"
#include
int main() {
uWS::ClientApp app({
.open = [](/*auto *ws*/) {
std::cout << "Hello and welcome to client" << std::endl;
},
.message = [](/*auto *ws, auto message*/) {
},
.close = [](/*auto *ws*/) {
std::cout << "bye" << std::endl;
}
});
app.connect("ws://localhost:3000", "protocol");
app.run();
}
================================================
FILE: examples/Crc32.cpp
================================================
#include "App.h"
/* This is a good example for testing and showing the POST requests.
* Anything you post (either with content-length or using transfer-encoding: chunked) will
* be hashed with crc32 and sent back in the response. This example also shows how to deal with
* aborted requests. */
/* curl -H "Transfer-Encoding: chunked" --data-binary @video.mp4 http://localhost:3000 */
/* curl --data-binary @video.mp4 http://localhost:3000 */
/* crc32 video.mp4 */
/* Note that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support */
#include
#include
#include
uint32_t crc32(const char *s, size_t n, uint32_t crc = 0xFFFFFFFF) {
for (size_t i = 0; i < n; i++) {
unsigned char ch = static_cast(s[i]);
for (size_t j = 0; j < 8; j++) {
uint32_t b = (ch ^ crc) & 1;
crc >>= 1;
if (b) crc = crc ^ 0xEDB88320;
ch >>= 1;
}
}
return crc;
}
int main() {
uWS::SSLApp({
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
}).post("/*", [](auto *res, auto *req) {
/* Display the headers */
std::cout << " --- " << req->getUrl() << " --- " << std::endl;
for (auto [key, value] : *req) {
std::cout << key << ": " << value << std::endl;
}
auto isAborted = std::make_shared(false);
uint32_t crc = 0xFFFFFFFF;
res->onData([res, isAborted, crc](std::string_view chunk, bool isFin) mutable {
if (chunk.length()) {
crc = crc32(chunk.data(), chunk.length(), crc);
}
if (isFin && !*isAborted) {
std::stringstream s;
s << std::hex << (~crc) << std::endl;
res->end(s.str());
}
});
res->onAborted([isAborted]() {
*isAborted = true;
});
}).listen(3000, [](auto *listen_socket) {
if (listen_socket) {
std::cerr << "Listening on port " << 3000 << std::endl;
}
}).run();
std::cout << "Failed to listen on port 3000" << std::endl;
}
================================================
FILE: examples/EchoBody.cpp
================================================
#include "App.h"
/* Takes any method and echoes back the sent body. Can be used to test compliance of HTTP spec. */
/* This example is also a good benchmark for body echoing. */
int main() {
uWS::App().get("/*", [](auto *res, auto */*req*/) {
/* Technically the any route could be used likewise, but GET is optimized like this */
res->end();
}).any("/*", [](auto *res, auto */*req*/) {
std::unique_ptr buffer;
res->onData([res, buffer = std::move(buffer)](std::string_view chunk, bool isFin) mutable {
if (isFin) [[likely]] {
if (buffer.get()) [[unlikely]] {
buffer->append(chunk);
res->end(*buffer);
} else {
res->end(chunk);
}
} else {
if (!buffer.get()) {
buffer = std::make_unique(chunk);
} else {
buffer->append(chunk);
}
}
});
/* In this particular case we actually don't need to know this, as we only rely on RAII above. */
res->onAborted([]() {});
}).listen(3000, [](auto *listen_socket) {
if (listen_socket) {
std::cerr << "Listening on port " << 3000 << std::endl;
}
}).run();
std::cout << "Failed to listen on port 3000" << std::endl;
}
================================================
FILE: examples/EchoServer.cpp
================================================
/* We simply call the root header file "App.h", giving you uWS::App and uWS::SSLApp */
#include "App.h"
/* This is a simple WebSocket echo server example.
* You may compile it with "WITH_OPENSSL=1 make" or with "make" */
int main() {
/* ws->getUserData returns one of these */
struct PerSocketData {
/* Fill with user data */
};
/* Keep in mind that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support.
* You may swap to using uWS:App() if you don't need SSL */
uWS::App({
/* There are example certificates in uWebSockets.js repo */
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
}).ws("/*", {
/* Settings */
.compression = uWS::CompressOptions(uWS::DEDICATED_COMPRESSOR | uWS::DEDICATED_DECOMPRESSOR),
.maxPayloadLength = 100 * 1024 * 1024,
.idleTimeout = 16,
.maxBackpressure = 100 * 1024 * 1024,
.closeOnBackpressureLimit = false,
.resetIdleTimeoutOnSend = false,
.sendPingsAutomatically = true,
/* Handlers */
.upgrade = nullptr,
.open = [](auto */*ws*/) {
/* Open event here, you may access ws->getUserData() which points to a PerSocketData struct */
},
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
/* This is the opposite of what you probably want; compress if message is LARGER than 16 kb
* the reason we do the opposite here; compress if SMALLER than 16 kb is to allow for
* benchmarking of large message sending without compression */
/* Never mind, it changed back to never compressing for now */
ws->send(message, opCode, false);
},
.dropped = [](auto */*ws*/, std::string_view /*message*/, uWS::OpCode /*opCode*/) {
/* A message was dropped due to set maxBackpressure and closeOnBackpressureLimit limit */
},
.drain = [](auto */*ws*/) {
/* Check ws->getBufferedAmount() here */
},
.ping = [](auto */*ws*/, std::string_view) {
/* Not implemented yet */
},
.pong = [](auto */*ws*/, std::string_view) {
/* Not implemented yet */
},
.close = [](auto */*ws*/, int /*code*/, std::string_view /*message*/) {
/* You may access ws->getUserData() here */
}
}).listen(9001, [](auto *listen_socket) {
if (listen_socket) {
std::cout << "Listening on port " << 9001 << std::endl;
}
}).run();
}
================================================
FILE: examples/EchoServerThreaded.cpp
================================================
#include "App.h"
#include
#include
int main() {
/* ws->getUserData returns one of these */
struct PerSocketData {
};
/* Simple echo websocket server, using multiple threads */
std::vector threads(std::thread::hardware_concurrency());
std::transform(threads.begin(), threads.end(), threads.begin(), [](std::thread */*t*/) {
return new std::thread([]() {
/* Very simple WebSocket echo server */
uWS::App().ws("/*", {
/* Settings */
.compression = uWS::SHARED_COMPRESSOR,
.maxPayloadLength = 16 * 1024,
.idleTimeout = 10,
.maxBackpressure = 1 * 1024 * 1024,
/* Handlers */
.upgrade = nullptr,
.open = [](auto */*ws*/) {
},
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
ws->send(message, opCode);
},
.drain = [](auto */*ws*/) {
/* Check getBufferedAmount here */
},
.ping = [](auto */*ws*/, std::string_view) {
},
.pong = [](auto */*ws*/, std::string_view) {
},
.close = [](auto */*ws*/, int /*code*/, std::string_view /*message*/) {
}
}).listen(9001, [](auto *listen_socket) {
if (listen_socket) {
std::cout << "Thread " << std::this_thread::get_id() << " listening on port " << 9001 << std::endl;
} else {
std::cout << "Thread " << std::this_thread::get_id() << " failed to listen on port 9001" << std::endl;
}
}).run();
});
});
std::for_each(threads.begin(), threads.end(), [](std::thread *t) {
t->join();
});
}
================================================
FILE: examples/HelloWorld.cpp
================================================
#include "App.h"
/* Note that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support */
int main() {
/* Overly simple hello world app */
uWS::SSLApp({
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
}).get("/*", [](auto *res, auto */*req*/) {
res->end("Hello world!");
}).listen(3000, [](auto *listen_socket) {
if (listen_socket) {
std::cout << "Listening on port " << 3000 << std::endl;
}
}).run();
std::cout << "Failed to listen on port 3000" << std::endl;
}
================================================
FILE: examples/HelloWorldThreaded.cpp
================================================
#include "App.h"
#include "LocalCluster.h"
int main() {
/* Note that SSL is disabled unless you build with WITH_OPENSSL=1 */
uWS::LocalCluster({
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
},
[](uWS::SSLApp &app) {
/* Here this App instance is defined */
app.get("/*", [](auto *res, auto * /*req*/) {
res->end("Hello world!");
}).listen(3000, [](auto *listen_socket) {
if (listen_socket) {
/* Note that us_listen_socket_t is castable to us_socket_t */
std::cout << "Thread " << std::this_thread::get_id() << " listening on port " << us_socket_local_port(true, (struct us_socket_t *) listen_socket) << std::endl;
} else {
std::cout << "Thread " << std::this_thread::get_id() << " failed to listen on port 3000" << std::endl;
}
});
});
}
================================================
FILE: examples/Http3Server.cpp
================================================
#ifdef LIBUS_USE_QUIC
/* Do not rely on this API, it will change */
#include "Http3App.h"
#include
#include
/* This is an example serving a video over HTTP3, and echoing posted data back */
/* Todo: use onWritable and tryEnd instead of end */
int main() {
/* Read video file to memory */
std::ifstream file("video.mp4", std::ios::binary | std::ios::ate);
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector buffer(size);
if (!file.read(buffer.data(), size)) {
std::cout << "Failed to load video.mp4" << std::endl;
return 0;
}
/* We need a bootstrapping server that instructs
* the web browser to use HTTP3 */
(*new uWS::SSLApp({
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
})).get("/*", [&buffer](auto *res, auto *req) {
res->writeHeader("Alt-Svc", "h3=\":9004\"");
res->writeHeader("Alternative-Protocol", "quic:9004");
res->end("This is not HTTP3! Try refreshing (works in Firefox!)
");
}).listen(9004, [](auto *listen_socket) {
if (listen_socket) {
std::cout << "Bootstrapping server Listening on port " << 9004 << std::endl;
}
});
/* And we serve the video over HTTP3 */
uWS::H3App({
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
}).get("/*", [&buffer](auto *res, auto *req) {
res->end("");
}).get("/video.mp4", [&buffer](auto *res, auto *req) {
/* Send back a video */
res->end({&buffer[0], buffer.size()});
}).post("/*", [](auto *res, auto *req) {
std::cout << "Got POST request at " << req->getHeader(":path") << std::endl;
/* You also need to set onAborted if receiving data */
res->onData([res, bodyBuffer = (std::string *)nullptr](std::string_view chunk, bool isLast) mutable {
if (isLast) {
std::cout << "Sending back posted body now" << std::endl;
if (bodyBuffer) {
/* Send back the (chunked) body we got, as response */
bodyBuffer->append(chunk);
res->end(*bodyBuffer);
delete bodyBuffer;
} else {
/* Send back the body we got, as response (fast path) */
res->end(chunk);
}
} else {
/* Slow path */
if (!bodyBuffer) {
bodyBuffer = new std::string;
}
/* If we got the body in a chunk, buffer it up until whole */
bodyBuffer->append(chunk);
}
});
/* If you have pending, asynch work, you should abort such work in this callback */
res->onAborted([]() {
/* Again, just printing is not enough, you need to abort any pending work here
* so that nothing will call res->end, since the request was aborted and deleted */
printf("Stream was aborted!\n");
});
}).listen(9004, [](auto *listen_socket) {
if (listen_socket) {
std::cout << "HTTP/3 server Listening on port " << 9004 << std::endl;
}
}).run();
std::cout << "Failed to listen on port 9004" << std::endl;
}
#else
#include
int main() {
printf("Compile with WITH_QUIC=1 WITH_BORINGSSL=1 make in order to build this example\n");
}
#endif
================================================
FILE: examples/HttpServer.cpp
================================================
/* This is a simple HTTP(S) web server much like Python's SimpleHTTPServer */
#include
/* Helpers for this example */
#include "helpers/AsyncFileReader.h"
#include "helpers/AsyncFileStreamer.h"
#include "helpers/Middleware.h"
/* optparse */
#define OPTPARSE_IMPLEMENTATION
#include "helpers/optparse.h"
int main(int argc, char **argv) {
int option;
struct optparse options;
optparse_init(&options, argv);
struct optparse_long longopts[] = {
{"port", 'p', OPTPARSE_REQUIRED},
{"help", 'h', OPTPARSE_NONE},
{"passphrase", 'a', OPTPARSE_REQUIRED},
{"key", 'k', OPTPARSE_REQUIRED},
{"cert", 'c', OPTPARSE_REQUIRED},
{"dh_params", 'd', OPTPARSE_REQUIRED},
{0}
};
int port = 3000;
uWS::SocketContextOptions ssl_options = {};
while ((option = optparse_long(&options, longopts, nullptr)) != -1) {
switch (option) {
case 'p':
port = atoi(options.optarg);
break;
case 'a':
ssl_options.passphrase = options.optarg;
break;
case 'c':
ssl_options.cert_file_name = options.optarg;
break;
case 'k':
ssl_options.key_file_name = options.optarg;
break;
case 'd':
ssl_options.dh_params_file_name = options.optarg;
break;
case 'h':
case '?':
fail:
std::cout << "Usage: " << argv[0] << " [--help] [--port ] [--key ] [--cert ] [--passphrase ] [--dh_params ] " << std::endl;
return 0;
}
}
char *root = optparse_arg(&options);
if (!root) {
goto fail;
}
AsyncFileStreamer asyncFileStreamer(root);
/* Either serve over HTTP or HTTPS */
uWS::SocketContextOptions empty_ssl_options = {};
if (memcmp(&ssl_options, &empty_ssl_options, sizeof(empty_ssl_options))) {
/* HTTPS */
uWS::SSLApp(ssl_options).get("/*", [&asyncFileStreamer](auto *res, auto *req) {
serveFile(res, req);
asyncFileStreamer.streamFile(res, req->getUrl());
}).listen(port, [port, root](auto *token) {
if (token) {
std::cout << "Serving " << root << " over HTTPS a " << port << std::endl;
}
}).run();
} else {
/* HTTP */
uWS::App().get("/*", [&asyncFileStreamer](auto *res, auto *req) {
serveFile(res, req);
asyncFileStreamer.streamFile(res, req->getUrl());
}).listen(port, [port, root](auto *token) {
if (token) {
std::cout << "Serving " << root << " over HTTP a " << port << std::endl;
}
}).run();
}
std::cout << "Failed to listen to port " << port << std::endl;
}
================================================
FILE: examples/ParameterRoutes.cpp
================================================
#include "App.h"
/* Note that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support */
int main() {
/* Overly simple hello world app */
uWS::SSLApp({
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
}).get("/:first/static/:second", [](auto *res, auto *req) {
res->write("first is: ");
res->write(req->getParameter("first"));
res->write("
");
res->write("second is: ");
res->write(req->getParameter("second"));
res->end("
");
}).listen(3000, [](auto *listen_socket) {
if (listen_socket) {
std::cout << "Listening on port " << 3000 << std::endl;
}
}).run();
std::cout << "Failed to listen on port 3000" << std::endl;
}
================================================
FILE: examples/Precompress.cpp
================================================
#include "App.h"
int main() {
/* ws->getUserData returns one of these */
struct PerSocketData {
/* Fill with user data */
};
/* Keeping track of last prepared message */
uWS::PreparedMessage preparedMessage;
std::mutex m;
/* For demo, we create a thread that will update the precompressed message every second */
std::thread t2([&m, &preparedMessage]() {
for (int counter = 1; true; counter++) {
m.lock();
std::string newMessage = "Hello you are looking at message number " + std::to_string(counter) + " and this text should be precompressed";
/* Here the current preparedMessage is updated */
preparedMessage = uWS::Loop::get()->prepareMessage(newMessage, uWS::OpCode::TEXT, true);
m.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
});
uWS::App app;
app.ws("/*", {
/* You must only use SHARED_COMPRESSOR with precompression (can't use dedicated_compressor) */
.compression = uWS::CompressOptions(uWS::SHARED_COMPRESSOR | uWS::DEDICATED_DECOMPRESSOR),
/* Handlers */
.upgrade = nullptr,
.open = [](auto */*ws*/) {
/* Open event here, you may access ws->getUserData() which points to a PerSocketData struct */
},
.message = [&m, &preparedMessage, &app](auto *ws, std::string_view message, uWS::OpCode opCode) {
/* First respond by echoing what they send us, without compression */
ws->send(message, opCode, false);
/* Send last prepared message */
m.lock();
ws->sendPrepared(preparedMessage);
/* Using publish should also take preparedMessage */
ws->subscribe("test");
app.publishPrepared("test", preparedMessage);
ws->unsubscribe("test");
m.unlock();
},
.dropped = [](auto */*ws*/, std::string_view /*message*/, uWS::OpCode /*opCode*/) {
/* A message was dropped due to set maxBackpressure and closeOnBackpressureLimit limit */
},
.drain = [](auto */*ws*/) {
/* Check ws->getBufferedAmount() here */
},
.ping = [](auto */*ws*/, std::string_view) {
/* Not implemented yet */
},
.pong = [](auto */*ws*/, std::string_view) {
/* Not implemented yet */
},
.close = [](auto */*ws*/, int /*code*/, std::string_view /*message*/) {
/* You may access ws->getUserData() here */
}
}).listen(9001, [&t2](auto *listen_socket) {
if (listen_socket) {
std::cout << "Listening on port " << 9001 << std::endl;
}
}).run();
}
================================================
FILE: examples/README.md
================================================
# Examples
Make sure to also check out the JavaScript examples, the TypeDoc documentation browser and the Discussions tab here on GitHub. Much of what is true for the Node.js addon is true also for the C++ library.
================================================
FILE: examples/SecureGzipFileServer.cpp
================================================
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "App.h"
#ifndef UWS_NO_ZLIB
#include
#endif
#if defined(__linux__)
#include
#include
#endif
int cooldown = 0; // Seconds to sleep after reloading files (Linux only)
// Custom hasher for transparent lookup with std::string and std::string_view
struct StringViewHasher {
using is_transparent = void;
std::size_t operator()(const std::string& s) const { return std::hash{}(s); }
std::size_t operator()(std::string_view s) const { return std::hash{}(s); }
};
// Custom equality comparator for transparent lookup
struct StringViewEqual {
using is_transparent = void;
bool operator()(std::string_view lhs, std::string_view rhs) const { return lhs == rhs; }
};
// Global variables
std::unordered_map, StringViewHasher, StringViewEqual> file_map;
std::mutex map_mutex;
#if defined(__linux__)
int inotify_fd;
#endif
unsigned long fileSizes = 0;
// Loads file content; compresses with zlib if UWS_NO_ZLIB is not defined and compression is beneficial
std::pair load_file_content(const std::filesystem::path& path) {
std::ifstream file(path, std::ios::binary);
if (!file) {
std::cerr << "Failed to open file: " << path << std::endl;
return {"", false};
}
file.seekg(0, std::ios::end);
size_t size = file.tellg();
file.seekg(0, std::ios::beg);
std::string content(size, 0);
file.read(&content[0], size);
#ifndef UWS_NO_ZLIB
z_stream zs = {};
if (deflateInit2(&zs, Z_BEST_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY) == Z_OK) {
zs.next_in = reinterpret_cast(content.data());
zs.avail_in = content.size();
size_t bound = deflateBound(&zs, content.size());
std::string compressed(bound, 0);
zs.next_out = reinterpret_cast(&compressed[0]);
zs.avail_out = bound;
int ret = deflate(&zs, Z_FINISH);
if (ret == Z_STREAM_END) {
size_t compressed_size = zs.total_out;
deflateEnd(&zs);
compressed.resize(compressed_size);
if (compressed_size < size) {
fileSizes += compressed_size;
return {compressed, true};
}
} else {
deflateEnd(&zs);
}
}
#endif
fileSizes += size;
return {content, false};
}
// Loads all files from the root folder into the map; adds inotify watches on Linux if inotify_fd >= 0
void load_files(const std::string& root, int inotify_fd = -1) {
fileSizes = 0;
std::unordered_map, StringViewHasher, StringViewEqual> new_map;
for (const auto& entry : std::filesystem::recursive_directory_iterator(root)) {
if (entry.is_regular_file() && !entry.path().filename().string().starts_with(".")) {
std::string relative_path = "/" + std::filesystem::relative(entry.path(), root).generic_string();
auto [content, compressed] = load_file_content(entry.path());
new_map[relative_path] = {content, compressed};
#if defined(__linux__)
if (inotify_fd >= 0) {
inotify_add_watch(inotify_fd, entry.path().c_str(), IN_MODIFY);
}
#endif
} else if (entry.is_directory()) {
#if defined(__linux__)
if (inotify_fd >= 0) {
inotify_add_watch(inotify_fd, entry.path().c_str(), IN_CREATE | IN_DELETE | IN_MOVE);
}
#endif
}
}
{
std::lock_guard lock(map_mutex);
file_map = std::move(new_map);
std::cout << "Loaded " << (fileSizes / 1024 / 1024) << " MB of files into RAM" << std::endl;
}
}
#if defined(__linux__)
// Background thread to monitor inotify events and reload files on changes (Linux only)
void inotify_reloader_function(const std::string& root, int inotify_fd) {
char buffer[4096];
while (true) {
int length = read(inotify_fd, buffer, sizeof(buffer));
if (length < 0) {
std::cerr << "Error reading inotify events: " << strerror(errno) << std::endl;
break;
}
load_files(root, inotify_fd);
if (cooldown) {
std::cout << "Sleeping for " << cooldown << " seconds after reload" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(cooldown));
}
}
}
#endif
int main(int argc, char** argv) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " " << std::endl;
return 1;
}
std::string root = argv[1];
cooldown = std::stoi(argv[2]);
if (cooldown < 0) {
std::cerr << "Cooldown must be a non-negative integer" << std::endl;
return 1;
}
#if defined(__linux__)
inotify_fd = inotify_init();
if (inotify_fd < 0) {
std::cerr << "Failed to initialize inotify: " << strerror(errno) << std::endl;
return 1;
}
load_files(root, inotify_fd);
std::thread inotify_reloader(inotify_reloader_function, root, inotify_fd);
#else
load_files(root); // Load once at startup on macOS and Windows
#endif
uWS::App app;
// Static key for uWS handlers
static char handler_key;
// Add post and pre handlers to lock the mutex around event loop iterations
uWS::Loop::get()->addPostHandler(&handler_key, [](uWS::Loop* /*loop*/) {
std::lock_guard lock(map_mutex);
});
uWS::Loop::get()->addPreHandler(&handler_key, [](uWS::Loop* /*loop*/) {
std::lock_guard lock(map_mutex);
});
app.get("/", [](auto* res, auto* req) {
auto it = file_map.find(std::string_view("/index.html", 11));
if (it != file_map.end()) {
if (it->second.second) {
res->writeHeader("Content-Encoding", "gzip");
}
res->end(it->second.first);
} else {
res->writeStatus("404 Not Found");
res->end("Not Found");
}
});
app.get("/*", [](auto* res, auto* req) {
auto it = file_map.find(req->getUrl());
if (it != file_map.end()) {
if (it->second.second) {
res->writeHeader("Content-Encoding", "gzip");
}
res->end(it->second.first);
} else {
res->writeStatus("404 Not Found");
res->end("Not Found");
}
});
app.listen(8000, [](auto* token) {
if (token) {
std::cout << "Listening on port 8000" << std::endl;
}
});
app.run();
#if defined(__linux__)
inotify_reloader.join();
close(inotify_fd);
#endif
return 0;
}
================================================
FILE: examples/ServerName.cpp
================================================
#include "App.h"
/* Note that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support */
int main() {
/* The SSL context given in SSLApp constructor is the default / catch-all context */
uWS::SSLApp app = uWS::SSLApp({
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
}).get("/*", [](auto *res, auto */*req*/) {
res->end("Hello from catch-all context!");
}).addServerName("*.google.*", {
/* Following is the context for *.google.* domain */
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
}).domain("*.google.*").get("/*", [](auto *res, auto */*req*/) {
res->end("Hello from *.google.* context!");
}).listen(3000, [](auto *listenSocket) {
if (listenSocket) {
std::cout << "Listening on port " << 3000 << std::endl;
} else {
std::cout << "Failed to listen on port 3000" << std::endl;
}
}).run();
}
================================================
FILE: examples/SmokeTest.cpp
================================================
#include "App.h"
/* This is not an example; it is a smoke test used in CI testing */
struct Stream {
int offset;
bool aborted;
};
std::string constantChunk;
void streamData(auto *res, auto stream, int chunk) {
if (stream->aborted) {
return;
}
if (chunk < 1600) {
res->cork([res, stream, chunk]() {
auto ok = res->write(constantChunk);
if (ok) {
streamData(res, stream, chunk + 1);
return;
}
uWS::Loop::get()->defer([res, stream, chunk]() {
streamData(res, stream, chunk + 1);
});
});
} else {
res->cork([res]() {
res->end();
});
}
}
int main() {
for (int i = 0; i < 65536; i++) {
constantChunk.append("a", 1);
}
uWS::SSLApp({
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
}).get("/*", [](auto *res, auto */*req*/) {
auto stream = std::make_shared(0, false);
streamData(res, stream, 0);
res->onAborted([stream]() {
stream->aborted = true;
});
}).listen(3000, [](auto *listen_socket) {
if (listen_socket) {
std::cout << "Listening on port " << 3000 << std::endl;
}
}).run();
std::cout << "Failed to listen on port 3000" << std::endl;
}
================================================
FILE: examples/UpgradeAsync.cpp
================================================
/* We simply call the root header file "App.h", giving you uWS::App and uWS::SSLApp */
#include "App.h"
/* This is a simple WebSocket "async" upgrade example.
* You may compile it with "WITH_OPENSSL=1 make" or with "make" */
int main() {
/* ws->getUserData returns one of these */
struct PerSocketData {
/* Define your user data */
int something;
};
/* Keep in mind that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support.
* You may swap to using uWS:App() if you don't need SSL */
uWS::SSLApp({
/* There are example certificates in uWebSockets.js repo */
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
}).ws("/*", {
/* Settings */
.compression = uWS::SHARED_COMPRESSOR,
.maxPayloadLength = 16 * 1024,
.idleTimeout = 10,
.maxBackpressure = 1 * 1024 * 1024,
/* Handlers */
.upgrade = [](auto *res, auto *req, auto *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 {
std::string secWebSocketKey;
std::string secWebSocketProtocol;
std::string secWebSocketExtensions;
struct us_socket_context_t *context;
decltype(res) httpRes;
bool aborted = false;
} *upgradeData = new UpgradeData {
std::string(req->getHeader("sec-websocket-key")),
std::string(req->getHeader("sec-websocket-protocol")),
std::string(req->getHeader("sec-websocket-extensions")),
context,
res
};
/* We have to attach an abort handler for us to be aware
* of disconnections while we perform async tasks */
res->onAborted([=]() {
/* We don't implement any kind of cancellation here,
* so simply flag us as aborted */
upgradeData->aborted = true;
std::cout << "HTTP socket was closed before we upgraded it!" << std::endl;
});
/* 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!
*
* Also note that the code would be a lot simpler with capturing lambdas, maybe your
* database client has such a nice interface? Either way, here we go!*/
struct us_loop_t *loop = (struct us_loop_t *) uWS::Loop::get();
struct us_timer_t *delayTimer = us_create_timer(loop, 0, sizeof(UpgradeData *));
memcpy(us_timer_ext(delayTimer), &upgradeData, sizeof(UpgradeData *));
us_timer_set(delayTimer, [](struct us_timer_t *t) {
/* We wrote the upgradeData pointer to the timer's extension */
UpgradeData *upgradeData;
memcpy(&upgradeData, us_timer_ext(t), sizeof(UpgradeData *));
/* Were'nt we aborted before our async task finished? Okay, upgrade then! */
if (!upgradeData->aborted) {
std::cout << "Async task done, upgrading to WebSocket now!" << std::endl;
/* If you don't want to upgrade you can instead respond with custom HTTP here,
* such as res->writeStatus(...)->writeHeader(...)->end(...); or similar.*/
/* This call will immediately emit .open event */
upgradeData->httpRes->cork([upgradeData]() {
upgradeData->httpRes->template upgrade({
/* We initialize PerSocketData struct here */
.something = 13
}, upgradeData->secWebSocketKey,
upgradeData->secWebSocketProtocol,
upgradeData->secWebSocketExtensions,
upgradeData->context);
});
} else {
std::cout << "Async task done, but the HTTP socket was closed. Skipping upgrade to WebSocket!" << std::endl;
}
delete upgradeData;
us_timer_close(t);
}, 5000, 0);
},
.open = [](auto *ws) {
/* Open event here, you may access ws->getUserData() which points to a PerSocketData struct.
* Here we simply validate that indeed, something == 13 as set in upgrade handler. */
std::cout << "Something is: " << static_cast(ws->getUserData())->something << std::endl;
},
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
/* We simply echo whatever data we get */
ws->send(message, opCode);
},
.drain = [](auto */*ws*/) {
/* Check ws->getBufferedAmount() here */
},
.ping = [](auto */*ws*/, std::string_view) {
/* You don't need to handle this one, we automatically respond to pings as per standard */
},
.pong = [](auto */*ws*/, std::string_view) {
/* You don't need to handle this one either */
},
.close = [](auto */*ws*/, int /*code*/, std::string_view /*message*/) {
/* You may access ws->getUserData() here, but sending or
* doing any kind of I/O with the socket is not valid. */
}
}).listen(9001, [](auto *listen_socket) {
if (listen_socket) {
std::cout << "Listening on port " << 9001 << std::endl;
}
}).run();
}
================================================
FILE: examples/UpgradeSync.cpp
================================================
/* We simply call the root header file "App.h", giving you uWS::App and uWS::SSLApp */
#include "App.h"
/* This is a simple WebSocket "sync" upgrade example.
* You may compile it with "WITH_OPENSSL=1 make" or with "make" */
int main() {
/* ws->getUserData returns one of these */
struct PerSocketData {
/* Define your user data */
int something;
};
/* Keep in mind that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support.
* You may swap to using uWS:App() if you don't need SSL */
uWS::SSLApp({
/* There are example certificates in uWebSockets.js repo */
.key_file_name = "misc/key.pem",
.cert_file_name = "misc/cert.pem",
.passphrase = "1234"
}).ws("/*", {
/* Settings */
.compression = uWS::SHARED_COMPRESSOR,
.maxPayloadLength = 16 * 1024,
.idleTimeout = 10,
.maxBackpressure = 1 * 1024 * 1024,
/* Handlers */
.upgrade = [](auto *res, auto *req, auto *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 ws->getUserData().
* 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 */
res->template upgrade({
/* We initialize PerSocketData struct here */
.something = 13
}, req->getHeader("sec-websocket-key"),
req->getHeader("sec-websocket-protocol"),
req->getHeader("sec-websocket-extensions"),
context);
/* If you don't want to upgrade you can instead respond with custom HTTP here,
* such as res->writeStatus(...)->writeHeader(...)->end(...); or similar.*/
/* Performing async upgrade, such as checking with a database is a little more complex;
* see UpgradeAsync example instead. */
},
.open = [](auto *ws) {
/* Open event here, you may access ws->getUserData() which points to a PerSocketData struct.
* Here we simply validate that indeed, something == 13 as set in upgrade handler. */
std::cout << "Something is: " << static_cast(ws->getUserData())->something << std::endl;
},
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
/* We simply echo whatever data we get */
ws->send(message, opCode);
},
.drain = [](auto */*ws*/) {
/* Check ws->getBufferedAmount() here */
},
.ping = [](auto */*ws*/, std::string_view) {
/* You don't need to handle this one, we automatically respond to pings as per standard */
},
.pong = [](auto */*ws*/, std::string_view) {
/* You don't need to handle this one either */
},
.close = [](auto */*ws*/, int /*code*/, std::string_view /*message*/) {
/* You may access ws->getUserData() here, but sending or
* doing any kind of I/O with the socket is not valid. */
}
}).listen(9001, [](auto *listen_socket) {
if (listen_socket) {
std::cout << "Listening on port " << 9001 << std::endl;
}
}).run();
}
================================================
FILE: examples/helpers/AsyncFileReader.h
================================================
#include