Showing preview only (598K chars total). Download the full file or copy to clipboard to get everything.
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
================================================
<div align="center">
<img src="https://raw.githubusercontent.com/uNetworking/uWebSockets/master/misc/logo.svg" height="180" /><br>
<i>Simple, secure</i><sup><a href="https://github.com/uNetworking/uWebSockets/tree/master/fuzzing#fuzz-testing-of-various-parsers-and-mocked-examples">1</a></sup><i> & standards compliant</i><sup><a href="https://unetworking.github.io/uWebSockets.js/report.pdf">2</a></sup><i> web server for the most demanding</i><sup><a href="https://github.com/uNetworking/uWebSockets/tree/master/benchmarks#benchmark-driven-development">3</a></sup><i> of applications.</i> <a href="https://github.com/uNetworking/uWebSockets/blob/master/misc/READMORE.md">Read more...</a>
<br><br>
<a href="https://github.com/uNetworking/uWebSockets/releases"><img src="https://img.shields.io/github/v/release/uNetworking/uWebSockets"></a> <a href="https://osv.dev/list?q=uwebsockets&affected_only=true&page=1&ecosystem=OSS-Fuzz"><img src="https://oss-fuzz-build-logs.storage.googleapis.com/badges/uwebsockets.svg" /></a> <img src="https://img.shields.io/badge/est.-2016-green" />
</div>
<br><br>
### :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 messaging<sup><a href="https://github.com/uNetworking/uWebSockets/tree/master/benchmarks#benchmark-driven-development">3</a></sup>.
Furthermore, we partake in Google's OSS-Fuzz with a ~95% daily fuzzing coverage<sup><a href="https://github.com/uNetworking/uWebSockets/blob/master/misc/Screenshot_20210915-004009.png?raw=true">4</a></sup> 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 <a href="https://github.com/uNetworking/uWebSockets.js">µWebSockets.js</a>.
### :crossed_swords: Battle proven
We've been fully standards compliant with a perfect Autobahn|Testsuite score since 2016<sup><a href="https://unetworking.github.io/uWebSockets.js/report.pdf">2</a></sup>. µ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; <a href="https://github.com/uNetworking/uWebSockets/blob/master/misc/READMORE.md">read the user manual</a> and <a href="https://github.com/uNetworking/uWebSockets/tree/master/examples">see examples</a>. You can browse our <a href="https://unetworking.github.io/uWebSockets.js/generated/">TypeDoc</a> 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("<h1>Hello ")
->write(req->getParameter("name"))
->end("!</h1>");
}).ws<UserData>("/*", {
/* 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
<a href="https://github.com/uNetworking">uNetworking AB</a> is a Swedish consulting & contracting company dealing with anything related to µWebSockets; development, support and customer success.
Don't hesitate <a href="mailto:alexhultman@gmail.com">sending a mail</a> 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 <a href="https://github.com/uNetworking/uSockets">µSockets</a>, 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 <libusockets.h>
int SSL;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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 <libkern/OSByteOrder.h>
#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 <endian.h>
#endif
#include <stdint.h>
#include <libusockets.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include <time.h>
/** @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<true> *wState, void *s) {
/* We need a limit */
if (length > 16000) {
return true;
}
/* Return ok */
return false;
}
static bool setCompressed(uWS::WebSocketState<true> *wState, void *s) {
/* We support it */
return true;
}
static void forceClose(uWS::WebSocketState<true> *wState, void *s, std::string_view reason = {}) {
}
static bool handleFragment(char *data, size_t length, unsigned int remainingBytes, int opCode, bool fin, uWS::WebSocketState<true> *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 <iostream>
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<true> 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<true, Impl>::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<true, Impl>::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 <libusockets.h>
int SSL;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
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 <time.h>
#include <iostream>
/* 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<PerSocketData>("/*", {
/* 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<std::string> 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<PerSocketData>("/*", {
/* 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 <iostream>
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 <iostream>
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 <sstream>
#include <cstdint>
#include <cstddef>
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<unsigned char>(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<bool>(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<std::string> 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<std::string>(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<PerSocketData>("/*", {
/* 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 <thread>
#include <algorithm>
int main() {
/* ws->getUserData returns one of these */
struct PerSocketData {
};
/* Simple echo websocket server, using multiple threads */
std::vector<std::thread *> 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<PerSocketData>("/*", {
/* 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 <iostream>
#include <fstream>
/* 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<char> 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("<html><h1>This is not HTTP3! Try refreshing (works in Firefox!)</h1></html>");
}).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("<html><h1>Welcome to HTTP3! <a href=\"video.mp4\">Go see a movie</a></html></h1>");
}).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 <stdio.h>
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 <App.h>
/* 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 <port>] [--key <ssl key>] [--cert <ssl cert>] [--passphrase <ssl key passphrase>] [--dh_params <ssl dh params file>] <public root>" << 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("<h1>first is: ");
res->write(req->getParameter("first"));
res->write("</h1>");
res->write("<h1>second is: ");
res->write(req->getParameter("second"));
res->end("</h1>");
}).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<PerSocketData>("/*", {
/* 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 <iostream>
#include <unordered_map>
#include <mutex>
#include <thread>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <string>
#include <cstdint>
#include <cstring>
#include "App.h"
#ifndef UWS_NO_ZLIB
#include <zlib.h>
#endif
#if defined(__linux__)
#include <sys/inotify.h>
#include <unistd.h>
#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<std::string_view>{}(s); }
std::size_t operator()(std::string_view s) const { return std::hash<std::string_view>{}(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<std::string, std::pair<std::string, bool>, 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<std::string, bool> 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<Bytef*>(content.data());
zs.avail_in = content.size();
size_t bound = deflateBound(&zs, content.size());
std::string compressed(bound, 0);
zs.next_out = reinterpret_cast<Bytef*>(&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<std::string, std::pair<std::string, bool>, 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<std::mutex> 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] << " <root_folder> <cooldown>" << 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<std::mutex> lock(map_mutex);
});
uWS::Loop::get()->addPreHandler(&handler_key, [](uWS::Loop* /*loop*/) {
std::lock_guard<std::mutex> 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<Stream>(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<PerSocketData>("/*", {
/* 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<PerSocketData>({
/* 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<PerSocketData *>(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<PerSocketData>("/*", {
/* 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<PerSocketData>({
/* 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<PerSocketData *>(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 <map>
#include <cstring>
#include <fstream>
#include <sstream>
#include <iostream>
#include <future>
/* This is just a very simple and inefficient demo of async responses,
* please do roll your own variant or use a database or Node.js's async
* features instead of this really bad demo */
struct AsyncFileReader {
private:
/* The cache we have in memory for this file */
std::string cache;
int cacheOffset;
bool hasCache;
/* The pending async file read (yes we only support one pending read) */
std::function<void(std::string_view)> pendingReadCb;
int fileSize;
std::string fileName;
std::ifstream fin;
uWS::Loop *loop;
public:
/* Construct a demo async. file reader for fileName */
AsyncFileReader(std::string fileName) : fileName(fileName) {
fin.open(fileName, std::ios::binary);
// get fileSize
fin.seekg(0, fin.end);
fileSize = fin.tellg();
//std::cout << "File size is: " << fileSize << std::endl;
// cache up 1 mb!
cache.resize(1024 * 1024);
//std::cout << "Caching 1 MB at offset = " << 0 << std::endl;
fin.seekg(0, fin.beg);
fin.read(cache.data(), cache.length());
cacheOffset = 0;
hasCache = true;
// get loop for thread
loop = uWS::Loop::get();
}
/* Returns any data already cached for this offset */
std::string_view peek(int offset) {
/* Did we hit the cache? */
if (hasCache && offset >= cacheOffset && ((offset - cacheOffset) < cache.length())) {
/* Cache hit */
//std::cout << "Cache hit!" << std::endl;
/*if (fileSize - offset < cache.length()) {
std::cout << "LESS THAN WHAT WE HAVE!" << std::endl;
}*/
int chunkSize = std::min<int>(fileSize - offset, cache.length() - offset + cacheOffset);
return std::string_view(cache.data() + offset - cacheOffset, chunkSize);
} else {
/* Cache miss */
//std::cout << "Cache miss!" << std::endl;
return std::string_view(nullptr, 0);
}
}
/* Asynchronously request more data at offset */
void request(int offset, std::function<void(std::string_view)> cb) {
// in this case, what do we do?
// we need to queue up this chunk request and callback!
// if queue is full, either block or close the connection via abort!
if (!hasCache) {
// already requesting a chunk!
std::cout << "ERROR: already requesting a chunk!" << std::endl;
return;
}
// disable cache
hasCache = false;
std::async(std::launch::async, [this, cb, offset]() {
//std::cout << "ASYNC Caching 1 MB at offset = " << offset << std::endl;
// den har stängts! öppna igen!
if (!fin.good()) {
fin.close();
//std::cout << "Reopening fin!" << std::endl;
fin.open(fileName, std::ios::binary);
}
fin.seekg(offset, fin.beg);
fin.read(cache.data(), cache.length());
cacheOffset = offset;
loop->defer([this, cb, offset]() {
int chunkSize = std::min<int>(cache.length(), fileSize - offset);
// båda dessa sker, wtf?
if (chunkSize == 0) {
std::cout << "Zero size!?" << std::endl;
}
if (chunkSize != cache.length()) {
std::cout << "LESS THAN A CACHE 1 MB!" << std::endl;
}
hasCache = true;
cb(std::string_view(cache.data(), chunkSize));
});
});
}
/* Abort any pending async. request */
void abort() {
}
int getFileSize() {
return fileSize;
}
};
================================================
FILE: examples/helpers/AsyncFileStreamer.h
================================================
#include <filesystem>
struct AsyncFileStreamer {
std::map<std::string_view, AsyncFileReader *> asyncFileReaders;
std::string root;
AsyncFileStreamer(std::string root) : root(root) {
// for all files in this path, init the map of AsyncFileReaders
updateRootCache();
}
void updateRootCache() {
// todo: if the root folder changes, we want to reload the cache
for(auto &p : std::filesystem::recursive_directory_iterator(root)) {
std::string url = p.path().string().substr(root.length());
if (url == "/index.html") {
url = "/";
}
char *key = new char[url.length()];
memcpy(key, url.data(), url.length());
asyncFileReaders[std::string_view(key, url.length())] = new AsyncFileReader(p.path().string());
}
}
template <bool SSL>
void streamFile(uWS::HttpResponse<SSL> *res, std::string_view url) {
auto it = asyncFileReaders.find(url);
if (it == asyncFileReaders.end()) {
std::cout << "Did not find file: " << url << std::endl;
} else {
streamFile(res, it->second);
}
}
template <bool SSL>
static void streamFile(uWS::HttpResponse<SSL> *res, AsyncFileReader *asyncFileReader) {
/* Peek from cache */
std::string_view chunk = asyncFileReader->peek(res->getWriteOffset());
auto remaining_data = asyncFileReader->getFileSize() - res->getWriteOffset();
if (!chunk.length() || res->tryEnd(chunk, remaining_data).first) {
/* Request new chunk */
// todo: we need to abort this callback if peer closed!
// this also means Loop::defer needs to support aborting (functions should embedd an atomic boolean abort or something)
// Loop::defer(f) -> integer
// Loop::abort(integer)
// hmm? no?
// us_socket_up_ref eftersom vi delar ägandeskapet
if (chunk.length() < remaining_data) {
asyncFileReader->request(res->getWriteOffset(), [res, asyncFileReader](std::string_view chunk) {
// check if we were closed in the mean time
//if (us_socket_is_closed()) {
// free it here
//return;
//}
/* We were aborted for some reason */
if (!chunk.length()) {
// todo: make sure to check for is_closed internally after all callbacks!
res->close();
} else {
AsyncFileStreamer::streamFile(res, asyncFileReader);
}
});
}
} else {
/* We failed writing everything, so let's continue when we can */
res->onWritable([res, asyncFileReader](int offset) {
// här kan skiten avbrytas!
AsyncFileStreamer::streamFile(res, asyncFileReader);
// todo: I don't really know what this is supposed to mean?
return false;
})->onAborted([]() {
std::cout << "ABORTED!" << std::endl;
});
}
}
};
================================================
FILE: examples/helpers/Middleware.h
================================================
/* Middleware to fill out content-type */
inline bool hasExt(std::string_view file, std::string_view ext) {
if (ext.size() > file.size()) {
return false;
}
return std::equal(ext.rbegin(), ext.rend(), file.rbegin());
}
/* This should be a filter / middleware like app.use(handler) */
template <bool SSL>
uWS::HttpResponse<SSL> *serveFile(uWS::HttpResponse<SSL> *res, uWS::HttpRequest *req) {
res->writeStatus(uWS::HTTP_200_OK);
if (hasExt(req->getUrl(), ".svg")) {
res->writeHeader("Content-Type", "image/svg+xml");
}
return res;
}
================================================
FILE: examples/helpers/optparse.h
================================================
/* Nicked from third-party https://github.com/skeeto/optparse 2018-09-24 */
/* µWebSockets is not the origin of this software file */
/* ------------------------------------------------------ */
/* Optparse --- portable, reentrant, embeddable, getopt-like option parser
*
* This is free and unencumbered software released into the public domain.
*
* To get the implementation, define OPTPARSE_IMPLEMENTATION.
* Optionally define OPTPARSE_API to control the API's visibility
* and/or linkage (static, __attribute__, __declspec).
*
* The POSIX getopt() option parser has three fatal flaws. These flaws
* are solved by Optparse.
*
* 1) Parser state is stored entirely in global variables, some of
* which are static and inaccessible. This means only one thread can
* use getopt(). It also means it's not possible to recursively parse
* nested sub-arguments while in the middle of argument parsing.
* Optparse fixes this by storing all state on a local struct.
*
* 2) The POSIX standard provides no way to properly reset the parser.
* This means for portable code that getopt() is only good for one
* run, over one argv with one option string. It also means subcommand
* options cannot be processed with getopt(). Most implementations
* provide a method to reset the parser, but it's not portable.
* Optparse provides an optparse_arg() function for stepping over
* subcommands and continuing parsing of options with another option
* string. The Optparse struct itself can be passed around to
* subcommand handlers for additional subcommand option parsing. A
* full reset can be achieved by with an additional optparse_init().
*
* 3) Error messages are printed to stderr. This can be disabled with
* opterr, but the messages themselves are still inaccessible.
* Optparse solves this by writing an error message in its errmsg
* field. The downside to Optparse is that this error message will
* always be in English rather than the current locale.
*
* Optparse should be familiar with anyone accustomed to getopt(), and
* it could be a nearly drop-in replacement. The option string is the
* same and the fields have the same names as the getopt() global
* variables (optarg, optind, optopt).
*
* Optparse also supports GNU-style long options with optparse_long().
* The interface is slightly different and simpler than getopt_long().
*
* By default, argv is permuted as it is parsed, moving non-option
* arguments to the end. This can be disabled by setting the `permute`
* field to 0 after initialization.
*/
#ifndef OPTPARSE_H
#define OPTPARSE_H
#ifndef OPTPARSE_API
# define OPTPARSE_API
#endif
struct optparse {
char **argv;
int permute;
int optind;
int optopt;
char *optarg;
char errmsg[64];
int subopt;
};
enum optparse_argtype {
OPTPARSE_NONE,
OPTPARSE_REQUIRED,
OPTPARSE_OPTIONAL
};
struct optparse_long {
const char *longname;
int shortname;
enum optparse_argtype argtype;
};
/**
* Initializes the parser state.
*/
OPTPARSE_API
void optparse_init(struct optparse *options, char **argv);
/**
* Read the next option in the argv array.
* @param optstring a getopt()-formatted option string.
* @return the next option character, -1 for done, or '?' for error
*
* Just like getopt(), a character followed by no colons means no
* argument. One colon means the option has a required argument. Two
* colons means the option takes an optional argument.
*/
OPTPARSE_API
int optparse(struct optparse *options, const char *optstring);
/**
* Handles GNU-style long options in addition to getopt() options.
* This works a lot like GNU's getopt_long(). The last option in
* longopts must be all zeros, marking the end of the array. The
* longindex argument may be NULL.
*/
OPTPARSE_API
int optparse_long(struct optparse *options,
const struct optparse_long *longopts,
int *longindex);
/**
* Used for stepping over non-option arguments.
* @return the next non-option argument, or NULL for no more arguments
*
* Argument parsing can continue with optparse() after using this
* function. That would be used to parse the options for the
* subcommand returned by optparse_arg(). This function allows you to
* ignore the value of optind.
*/
OPTPARSE_API
char *optparse_arg(struct optparse *options);
/* Implementation */
#ifdef OPTPARSE_IMPLEMENTATION
#define OPTPARSE_MSG_INVALID "invalid option"
#define OPTPARSE_MSG_MISSING "option requires an argument"
#define OPTPARSE_MSG_TOOMANY "option takes no arguments"
static int
optparse_error(struct optparse *options, const char *msg, const char *data)
{
unsigned p = 0;
const char *sep = " -- '";
while (*msg)
options->errmsg[p++] = *msg++;
while (*sep)
options->errmsg[p++] = *sep++;
while (p < sizeof(options->errmsg) - 2 && *data)
options->errmsg[p++] = *data++;
options->errmsg[p++] = '\'';
options->errmsg[p++] = '\0';
return '?';
}
OPTPARSE_API
void
optparse_init(struct optparse *options, char **argv)
{
options->argv = argv;
options->permute = 1;
options->optind = 1;
options->subopt = 0;
options->optarg = 0;
options->errmsg[0] = '\0';
}
static int
optparse_is_dashdash(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
}
static int
optparse_is_shortopt(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
}
static int
optparse_is_longopt(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
}
static void
optparse_permute(struct optparse *options, int index)
{
char *nonoption = options->argv[index];
int i;
for (i = index; i < options->optind - 1; i++)
options->argv[i] = options->argv[i + 1];
options->argv[options->optind - 1] = nonoption;
}
static int
optparse_argtype(const char *optstring, char c)
{
int count = OPTPARSE_NONE;
if (c == ':')
return -1;
for (; *optstring && c != *optstring; optstring++);
if (!*optstring)
return -1;
if (optstring[1] == ':')
count += optstring[2] == ':' ? 2 : 1;
return count;
}
OPTPARSE_API
int
optparse(struct optparse *options, const char *optstring)
{
int type;
char *next;
char *option = options->argv[options->optind];
options->errmsg[0] = '\0';
options->optopt = 0;
options->optarg = 0;
if (option == 0) {
return -1;
} else if (optparse_is_dashdash(option)) {
options->optind++; /* consume "--" */
return -1;
} else if (!optparse_is_shortopt(option)) {
if (options->permute) {
int index = options->optind++;
int r = optparse(options, optstring);
optparse_permute(options, index);
options->optind--;
return r;
} else {
return -1;
}
}
option += options->subopt + 1;
options->optopt = option[0];
type = optparse_argtype(optstring, option[0]);
next = options->argv[options->optind + 1];
switch (type) {
case -1: {
char str[2] = {0, 0};
str[0] = option[0];
options->optind++;
return optparse_error(options, OPTPARSE_MSG_INVALID, str);
}
case OPTPARSE_NONE:
if (option[1]) {
options->subopt++;
} else {
options->subopt = 0;
options->optind++;
}
return option[0];
case OPTPARSE_REQUIRED:
options->subopt = 0;
options->optind++;
if (option[1]) {
options->optarg = option + 1;
} else if (next != 0) {
options->optarg = next;
options->optind++;
} else {
char str[2] = {0, 0};
str[0] = option[0];
options->optarg = 0;
return optparse_error(options, OPTPARSE_MSG_MISSING, str);
}
return option[0];
case OPTPARSE_OPTIONAL:
options->subopt = 0;
options->optind++;
if (option[1])
options->optarg = option + 1;
else
options->optarg = 0;
return option[0];
}
return 0;
}
OPTPARSE_API
char *
optparse_arg(struct optparse *options)
{
char *option = options->argv[options->optind];
options->subopt = 0;
if (option != 0)
options->optind++;
return option;
}
static int
optparse_longopts_end(const struct optparse_long *longopts, int i)
{
return !longopts[i].longname && !longopts[i].shortname;
}
static void
optparse_from_long(const struct optparse_long *longopts, char *optstring)
{
char *p = optstring;
int i;
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
if (longopts[i].shortname) {
int a;
*p++ = longopts[i].shortname;
for (a = 0; a < (int)longopts[i].argtype; a++)
*p++ = ':';
}
}
*p = '\0';
}
/* Unlike strcmp(), handles options containing "=". */
static int
optparse_longopts_match(const char *longname, const char *option)
{
const char *a = option, *n = longname;
if (longname == 0)
return 0;
for (; *a && *n && *a != '='; a++, n++)
if (*a != *n)
return 0;
return *n == '\0' && (*a == '\0' || *a == '=');
}
/* Return the part after "=", or NULL. */
static char *
optparse_longopts_arg(char *option)
{
for (; *option && *option != '='; option++);
if (*option == '=')
return option + 1;
else
return 0;
}
static int
optparse_long_fallback(struct optparse *options,
const struct optparse_long *longopts,
int *longindex)
{
int result;
char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
optparse_from_long(longopts, optstring);
result = optparse(options, optstring);
if (longindex != 0) {
*longindex = -1;
if (result != -1) {
int i;
for (i = 0; !optparse_longopts_end(longopts, i); i++)
if (longopts[i].shortname == options->optopt)
*longindex = i;
}
}
return result;
}
OPTPARSE_API
int
optparse_long(struct optparse *options,
const struct optparse_long *longopts,
int *longindex)
{
int i;
char *option = options->argv[options->optind];
if (option == 0) {
return -1;
} else if (optparse_is_dashdash(option)) {
options->optind++; /* consume "--" */
return -1;
} else if (optparse_is_shortopt(option)) {
return optparse_long_fallback(options, longopts, longindex);
} else if (!optparse_is_longopt(option)) {
if (options->permute) {
int index = options->optind++;
int r = optparse_long(options, longopts, longindex);
optparse_permute(options, index);
options->optind--;
return r;
} else {
return -1;
}
}
/* Parse as long option. */
options->errmsg[0] = '\0';
options->optopt = 0;
options->optarg = 0;
option += 2; /* skip "--" */
options->optind++;
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
const char *name = longopts[i].longname;
if (optparse_longopts_match(name, option)) {
char *arg;
if (longindex)
*longindex = i;
options->optopt = longopts[i].shortname;
arg = optparse_longopts_arg(option);
if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) {
return optparse_error(options, OPTPARSE_MSG_TOOMANY, name);
} if (arg != 0) {
options->optarg = arg;
} else if (longopts[i].argtype == OPTPARSE_REQUIRED) {
options->optarg = options->argv[options->optind];
if (options->optarg == 0)
return optparse_error(options, OPTPARSE_MSG_MISSING, name);
else
options->optind++;
}
return options->optopt;
}
}
return optparse_error(options, OPTPARSE_MSG_INVALID, option);
}
#endif /* OPTPARSE_IMPLEMENTATION */
#endif /* OPTPARSE_H */
================================================
FILE: fuzzing/AsyncEpollHelloWorld.cpp
================================================
/* We rely on wrapped syscalls */
#include "libEpollFuzzer/epoll_fuzzer.h"
#include "App.h"
/* We keep this one for teardown later on */
struct us_listen_socket_t *listen_socket;
/* This test is run by libEpollFuzzer */
void test() {
{
/* 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 */
auto app = uWS::App({
/* There are example certificates in uWebSockets.js repo */
.key_file_name = "../misc/key.pem",
.cert_file_name = "../misc/cert.pem",
.passphrase = "1234"
}).get("/*", [](auto *res, auto *req) {
auto aborted = std::make_shared<bool>();
*aborted = false;
res->onAborted([aborted]() {
*aborted = true;
});
uWS::Loop::get()->defer([res, aborted]() {
if (!*aborted) {
res->cork([res, aborted]() {
// Todo: also test upgrade to websocket here
res->end("Hello async!");
});
}
});
}).listen(9001, [](auto *listenSocket) {
listen_socket = listenSocket;
});
app.run();
}
uWS::Loop::get()->free();
}
/* Thus function should shutdown the event-loop and let the test fall through */
void teardown() {
/* If we are called twice there's a bug (it potentially could if
* all open sockets cannot be error-closed in one epoll_wait call).
* But we only allow 1k FDs and we have a buffer of 1024 from epoll_wait */
if (!listen_socket) {
exit(-1);
}
/* We might have open sockets still, and these will be error-closed by epoll_wait */
// us_socket_context_close - close all open sockets created with this socket context
if (listen_socket) {
us_listen_socket_close(0, listen_socket);
listen_socket = NULL;
}
}
================================================
FILE: fuzzing/AsyncEpollHelloWorld.dict
================================================
"get"
"post"
"get /"
"http/1.1"
"upgrade: websocket"
"\x0D\x0A"
"sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ=="
"sec-websocket-version: 13"
"get / http/1.1"
"sec-websocket-extensions: permessage-deflate"
"sec-websocket-protocol: "
" "
================================================
FILE: fuzzing/EpollEchoServer.cpp
================================================
/* We rely on wrapped syscalls */
#include "libEpollFuzzer/epoll_fuzzer.h"
#include "App.h"
/* We keep this one for teardown later on */
struct us_listen_socket_t *listen_socket;
/* This test is run by libEpollFuzzer */
void test() {
struct PerSocketData {
int nothing;
std::shared_ptr<bool> valid;
};
/* First byte determines what compressor to use */
unsigned char compressorByte;
if (consume_byte(&compressorByte)) {
//uWS::Loop::get()->free();
return;
}
uWS::CompressOptions compressors[] = {
uWS::DISABLED,
uWS::SHARED_COMPRESSOR,
uWS::DEDICATED_COMPRESSOR_3KB,
uWS::DEDICATED_COMPRESSOR_4KB,
uWS::DEDICATED_COMPRESSOR_8KB,
uWS::DEDICATED_COMPRESSOR_16KB,
uWS::DEDICATED_COMPRESSOR_32KB,
uWS::DEDICATED_COMPRESSOR_64KB,
uWS::DEDICATED_COMPRESSOR_128KB,
uWS::DEDICATED_COMPRESSOR_256KB
};
uWS::CompressOptions compressor = compressors[compressorByte % 10];
{
auto app = uWS::App().ws<PerSocketData>("/broadcast", {
/* Settings */
.compression = compressor,
/* We want this to be low so that we can hit it, yet bigger than 256 */
.maxPayloadLength = 300,
.idleTimeout = 12,
/* Handlers */
.open = [](auto *ws) {
/* Subscribe to anything */
ws->subscribe(/*req->getHeader(*/"topic"/*)*/);
},
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
if (message.length() && message[0] == 'C') {
ws->close();
} else if (message.length() && message[0] == 'E') {
ws->end(1006);
} else {
/* Publish to topic sent by message */
ws->publish(message, message, opCode, true);
if (message.length() && message[0] == 'U') {
ws->unsubscribe(message);
}
}
},
.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) {
/* Cause reported crash */
ws->close();
}
}).ws<PerSocketData>("/*", {
/* Settings */
.compression = compressor,
/* We want this to be low so that we can hit it, yet bigger than 256 */
.maxPayloadLength = 300,
.idleTimeout = 12,
/* Handlers */
.open = [](auto *ws) {
ws->getUserData()->valid.reset(new bool{true});
//if (req->getHeader("close_me").length()) {
// ws->close();
//} else if (req->getHeader("end_me").length()) {
// ws->end(1006);
//}
},
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
if (message.length() > 300) {
/* Inform the sanitizer of the fault */
fprintf(stderr, "Too long message passed\n");
free((void *) -1);
}
if (message.length() && message[0] == 'C') {
ws->close();
} else if (message.length() && message[0] == 'E') {
ws->end(1006);
} else {
ws->send(message, opCode, true);
}
},
.drain = [](auto *ws) {
/* Check getBufferedAmount here */
},
.ping = [](auto *ws, std::string_view) {
/* Here we test send and end while uncorked, by having them send from deferred */
PerSocketData *psd = (PerSocketData *) ws->getUserData();
uWS::Loop::get()->defer([ws, valid = psd->valid]() {
if (*valid.get()) {
/* We haven't been closed */
ws->send("Hello!", uWS::TEXT, false);
ws->end(1000);
}
});
},
.pong = [](auto *ws, std::string_view) {
},
.close = [](auto *ws, int code, std::string_view message) {
(*ws->getUserData()->valid.get()) = false;
}
}).listen(9001, [](us_listen_socket_t *listenSocket) {
listen_socket = listenSocket;
});
app.run();
}
uWS::Loop::get()->free();
}
/* Thus function should shutdown the event-loop and let the test fall through */
void teardown() {
/* If we are called twice there's a bug (it potentially could if
* all open sockets cannot be error-closed in one epoll_wait call).
* But we only allow 1k FDs and we have a buffer of 1024 from epoll_wait */
if (!listen_socket) {
exit(-1);
}
/* We might have open sockets still, and these will be error-closed by epoll_wait */
// us_socket_context_close - close all open sockets created with this socket context
if (listen_socket) {
us_listen_socket_close(0, listen_socket);
listen_socket = NULL;
}
}
================================================
FILE: fuzzing/EpollEchoServer.dict
================================================
"get"
"post"
"get /"
"http/1.1"
"upgrade: websocket"
"\x0D\x0A"
"sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ=="
"sec-websocket-version: 13"
"get / http/1.1"
"sec-websocket-extensions: permessage-deflate"
"sec-websocket-protocol: "
" "
================================================
FILE: fuzzing/EpollEchoServerPubSub.cpp
================================================
/* We rely on wrapped syscalls */
#include "libEpollFuzzer/epoll_fuzzer.h"
#include "App.h"
#include <vector>
/* We keep this one for teardown later on */
struct us_listen_socket_t *listen_socket;
/* This test is run by libEpollFuzzer */
void test() {
/* ws->getUserData returns one of these */
struct PerSocketData {
/* Fill with user data */
std::vector<std::string> 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<PerSocketData>("/*", {
/* Settings */
.compression = uWS::DISABLED,
.maxPayloadLength = 512, // also have a low value here for fuzzing
.idleTimeout = 60,
.maxBackpressure = 128, // we want a low number so that we can reach this in fuzzing
.closeOnBackpressureLimit = false, // this one could be tested as well
.resetIdleTimeoutOnSend = true, // and this
.sendPingsAutomatically = false, // and this
/* 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 < 100; 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[++perSocketData->nr % 100], 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();
}
/* Thus function should shutdown the event-loop and let the test fall through */
void teardown() {
/* If we are called twice there's a bug (it potentially could if
* all open sockets cannot be error-closed in one epoll_wait call).
* But we only allow 1k FDs and we have a buffer of 1024 from epoll_wait */
if (!listen_socket) {
exit(-1);
}
/* We might have open sockets still, and these will be error-closed by epoll_wait */
// us_socket_context_close - close all open sockets created with this socket context
if (listen_socket) {
us_listen_socket_close(0, listen_socket);
listen_socket = NULL;
}
}
================================================
FILE: fuzzing/EpollEchoServerPubSub.dict
================================================
"get"
"post"
"get /"
"http/1.1"
"upgrade: websocket"
"\x0D\x0A"
"sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ=="
"sec-websocket-version: 13"
"get / http/1.1"
"sec-websocket-extensions: permessage-deflate"
"sec-websocket-protocol: "
" "
================================================
FILE: fuzzing/EpollHelloWorld.cpp
================================================
/* We rely on wrapped syscalls */
#include "libEpollFuzzer/epoll_fuzzer.h"
#include "App.h"
/* We keep this one for teardown later on */
struct us_listen_socket_t *listen_socket;
struct us_socket_t *client;
/* This test is run by libEpollFuzzer */
void test() {
/* 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 */
auto app = 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<PerSocketData>("/empty", {
/* Having no handlers here should not crash */
}).get("/*", [](auto *res, auto *req) {
if (req->getHeader("write").length()) {
res->writeStatus("200 OK")->writeHeader("write", "true")->write("Hello");
res->write(" world!");
res->end();
} else if (req->getQuery().length()) {
res->close();
} else {
res->end("Hello world!");
}
}).post("/*", [](auto *res, auto *req) {
res->onAborted([]() {
/* We might as well use this opportunity to stress the loop a bit */
uWS::Loop::get()->defer([]() {
});
});
res->onData([res](std::string_view chunk, bool isEnd) {
if (isEnd) {
res->cork([res, chunk]() {
res->write("something ahead");
res->end(chunk);
});
}
});
}).any("/:candy/*", [](auto *res, auto *req) {
if (req->getParameter(0).length() == 0) {
free((void *) -1);
}
/* Some invalid queries */
req->getParameter(30000);
req->getParameter((unsigned short) -34234);
req->getHeader("yhello");
req->getQuery();
req->getQuery("assd");
res->end("done");
}).ws<PerSocketData>("/*", {
/* Settings */
.compression = uWS::SHARED_COMPRESSOR,
.maxPayloadLength = 16 * 1024,
.idleTimeout = 12,
.maxBackpressure = 1024,
/* Handlers */
.open = [](auto *ws) {
/* Open event here, you may access ws->getUserData() which points to a PerSocketData struct */
ws->getNativeHandle();
ws->getRemoteAddressAsText();
us_poll_ext((struct us_poll_t *) ws);
},
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
ws->send(message, opCode, true);
},
.drain = [](auto *ws) {
/* Check ws->getBufferedAmount() here */
},
.ping = [](auto *ws, std::string_view) {
/* We use this to trigger the async/wakeup feature */
uWS::Loop::get()->defer([]() {
/* Do nothing */
});
},
.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 *listenSocket) {
listen_socket = listenSocket;
});
/* Here we want to stress the connect feature, since nothing else stresses it */
struct us_loop_t *loop = (struct us_loop_t *) uWS::Loop::get();
/* This function is stupid */
us_loop_iteration_number(loop);
struct us_socket_context_t *client_context = us_create_socket_context(0, loop, 0, {});
us_socket_context_timestamp(0, client_context);
client = us_socket_context_connect(0, client_context, "hostname", 5000, "localhost", 0, 0);
if (client) {
us_socket_is_established(0, client);
us_socket_local_port(0, client);
}
us_socket_context_on_connect_error(0, client_context, [](struct us_socket_t *s, int code) {
client = nullptr;
return s;
});
us_socket_context_on_open(0, client_context, [](struct us_socket_t *s, int is_client, char *ip, int ip_length) {
us_socket_flush(0, s);
return s;
});
us_socket_context_on_end(0, client_context, [](struct us_socket_t *s) {
/* Someone sent is a FIN, but we can still send data */
us_socket_write(0, s, "asdadasdasdasdaddfgdfhdfgdfg", 28, false);
return s;
});
us_socket_context_on_data(0, client_context, [](struct us_socket_t *s, char *data, int length) {
return s;
});
us_socket_context_on_writable(0, client_context, [](struct us_socket_t *s) {
/* Let's defer a close here */
us_socket_shutdown_read(0, s);
return s;
});
us_socket_context_on_close(0, client_context, [](struct us_socket_t *s, int code, void *reason) {
client = NULL;
return s;
});
/* Trigger some context functions */
app.addServerName("servername", {});
app.removeServerName("servername");
app.missingServerName(nullptr);
app.getNativeHandle();
app.run();
/* After done we also free the client context */
us_socket_context_free(0, client_context);
}
uWS::Loop::get()->setSilent(true);
uWS::Loop::get()->free();
}
/* Thus function should shutdown the event-loop and let the test fall through */
void teardown() {
/* If we are called twice there's a bug (it potentially could if
* all open sockets cannot be error-closed in one epoll_wait call).
* But we only allow 1k FDs and we have a buffer of 1024 from epoll_wait */
if (!listen_socket && !client) {
exit(-1);
}
if (client) {
us_socket_close(0, client, 0, 0);
client = NULL;
}
/* We might have open sockets still, and these will be error-closed by epoll_wait */
// us_socket_context_close - close all open sockets created with this socket context
if (listen_socket) {
us_listen_socket_close(0, listen_socket);
listen_socket = NULL;
}
}
================================================
FILE: fuzzing/EpollHelloWorld.dict
================================================
"get"
"post"
"get /"
"http/1.1"
"upgrade: websocket"
"\x0D\x0A"
"sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ=="
"sec-websocket-version: 13"
"get / http/1.1"
"sec-websocket-extensions: permessage-deflate"
"sec-websocket-protocol: "
" "
================================================
FILE: fuzzing/Extensions.cpp
================================================
/* This is a fuzz test of the websocket extensions parser */
#define WIN32_EXPORT
#include <cstdio>
#include <string>
#include <cstdlib>
/* We test the websocket extensions parser */
#include "../src/WebSocketExtensions.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
/* This one must not return shared compressor, or above 13 */
{
auto [negCompression, negCompressionWindow, negInflationWindow, response] = uWS::negotiateCompression(true, 13, 0, std::string_view((char *) data, size));
if (negCompression) {
/* If we want dedicated compression, we must not end up here! */
free((void *) (negCompressionWindow == 0));
/* Some more checks (freeing 0 does nothing) */
free((void *) (negCompressionWindow > 13));
free((void *) (negInflationWindow != 0));
free((void *) (negInflationWindow < 0 || negInflationWindow > 15 || negCompressionWindow < 0 || negCompressionWindow > 15));
}
}
/* This one must not return anything over 0 (only shared) */
{
auto [negCompression, negCompressionWindow, negInflationWindow, response] = uWS::negotiateCompression(true, 0, 0, std::string_view((char *) data, size));
if (negCompression) {
/* If we want shared compression, we must not end up here! */
free((void *) (negCompressionWindow != 0));
}
}
/* Whatever, this one must not negotiate anything */
{
auto [negCompression, negCompressionWindow, negInflationWindow, response] = uWS::negotiateCompression(false, 13, 15, std::string_view((char *) data, size));
if (negCompression) {
free((void *) -1);
}
}
return 0;
}
================================================
FILE: fuzzing/Http.cpp
================================================
/* This is a fuzz test of the http parser */
#define WIN32_EXPORT
#include "helpers.h"
/* We test the websocket parser */
#include "../src/HttpParser.h"
/* And the router */
#include "../src/HttpRouter.h"
/* Also ProxyParser */
#include "../src/ProxyParser.h"
struct StaticData {
struct RouterData {
};
uWS::HttpRouter<RouterData> router;
StaticData() {
router.add({"get"}, "/:hello/:hi", [](auto *h) mutable {
auto [paramsTop, params] = h->getParameters();
/* Something is horribly wrong */
if (paramsTop != 1 || !params[0].length() || !params[1].length()) {
exit(-1);
}
/* This route did handle it */
return true;
});
router.add({"post"}, "/:hello/:hi/*", [](auto *h) mutable {
auto [paramsTop, params] = h->getParameters();
/* Something is horribly wrong */
if (paramsTop != 1 || !params[0].length() || !params[1].length()) {
exit(-1);
}
/* This route did handle it */
return true;
});
router.add({"get"}, "/*", [](auto *h) mutable {
auto [paramsTop, params] = h->getParameters();
/* Something is horribly wrong */
if (paramsTop != -1) {
exit(-1);
}
/* This route did not handle it */
return false;
});
router.add({"get"}, "/hi", [](auto *h) mutable {
auto [paramsTop, params] = h->getParameters();
/* Something is horribly wrong */
if (paramsTop != -1) {
exit(-1);
}
/* This route did handle it */
return true;
});
}
} staticData;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
/* Create parser */
uWS::HttpParser httpParser;
/* User data */
void *user = (void *) 13;
/* If we are built with WITH_PROXY, pass a ProxyParser as reserved */
void *reserved = nullptr;
#ifdef UWS_WITH_PROXY
uWS::ProxyParser pp;
reserved = (void *) &pp;
#endif
/* Iterate the padded fuzz as chunks */
makeChunked(makePadded(data, size), size, [&httpParser, &user, reserved](const uint8_t *data, size_t size) {
/* We need at least 1 byte post padding */
if (size) {
size--;
} else {
/* We might be given zero length chunks */
return;
}
/* If user is null then ignore this chunk */
if (!user) {
return;
}
/* Parse it */
auto [err, returnedUser] = httpParser.consumePostPadded((char *) data, size, user, reserved, [reserved](void *s, uWS::HttpRequest *httpRequest) -> void * {
readBytes(httpRequest->getHeader(httpRequest->getUrl()));
readBytes(httpRequest->getMethod());
readBytes(httpRequest->getQuery());
readBytes(httpRequest->getQuery("hello"));
readBytes(httpRequest->getQuery(""));
//readBytes(httpRequest->getParameter(0));
#ifdef UWS_WITH_PROXY
auto *pp = (uWS::ProxyParser *) reserved;
readBytes(pp->getSourceAddress());
#endif
/* Route the method and URL in two passes */
staticData.router.getUserData() = {};
if (!staticData.router.route(httpRequest->getMethod(), httpRequest->getUrl())) {
/* It was not handled */
return nullptr;
}
for (auto p : *httpRequest) {
}
/* Return ok */
return s;
}, [](void *user, std::string_view data, bool fin) -> void * {
/* Return ok */
return user;
});
if (!returnedUser) {
/* It is of uttermost importance that if and when we return nullptr from the httpParser we must not
* ever use the httpParser ever again. It is in a broken state as returning nullptr is only used
* for signalling early closure. You must absolutely must throw it away. Here we just mark user as
* null so that we can ignore further chunks of data */
user = nullptr;
}
});
return 0;
}
================================================
FILE: fuzzing/Makefile
================================================
# You can select which sanitizer to use by setting this
SANITIZER ?= address
# These are set by OSS-Fuzz, we default to AddressSanitizer
CXXFLAGS ?= -DLIBUS_NO_SSL -fsanitize=$(SANITIZER),fuzzer
CFLAGS ?= -DLIBUS_NO_SSL
OUT ?= .
# These are fetched from libEpollFuzzer
WRAPPED_SYSCALLS = -Wl,--wrap=getpeername,--wrap=sendto,--wrap=send,--wrap=recv,--wrap=read,--wrap=listen,--wrap=getaddrinfo,--wrap=freeaddrinfo,--wrap=setsockopt,--wrap=fcntl,--wrap=bind,--wrap=socket,--wrap=epoll_wait,--wrap=epoll_create1,--wrap=timerfd_settime,--wrap=close,--wrap=accept4,--wrap=eventfd,--wrap=timerfd_create,--wrap=epoll_ctl,--wrap=shutdown
oss-fuzz:
# Copy dictionaries
cp *.dict $(OUT)
# libEpollFuzzer cases
# Compile uSockets without -flto
rm -rf *.o
$(CC) $(CFLAGS) -DLIBUS_NO_SSL -std=c11 -I../uSockets/src -O3 -c ../uSockets/src/*.c ../uSockets/src/eventing/*.c ../uSockets/src/crypto/*.c
# Link against object files
$(CXX) $(CXXFLAGS) $(WRAPPED_SYSCALLS) -std=c++17 -O3 -DUWS_MOCK_ZLIB -I../src -I../uSockets/src EpollHelloWorld.cpp -o $(OUT)/EpollHelloWorld $(LIB_FUZZING_ENGINE) *.o
rm -f EpollHelloWorld.o
$(CXX) $(CXXFLAGS) $(WRAPPED_SYSCALLS) -std=c++17 -O3 -DUWS_MOCK_ZLIB -I../src -I../uSockets/src AsyncEpollHelloWorld.cpp -o $(OUT)/AsyncEpollHelloWorld $(LIB_FUZZING_ENGINE) *.o
rm -f AsyncEpollHelloWorld.o
$(CXX) $(CXXFLAGS) $(WRAPPED_SYSCALLS) -std=c++17 -O3 -DUWS_MOCK_ZLIB -I../src -I../uSockets/src EpollEchoServer.cpp -o $(OUT)/EpollEchoServer $(LIB_FUZZING_ENGINE) *.o
rm -f EpollEchoServer.o
$(CXX) $(CXXFLAGS) $(WRAPPED_SYSCALLS) -std=c++17 -O3 -DUWS_MOCK_ZLIB -I../src -I../uSockets/src EpollEchoServerPubSub.cpp -o $(OUT)/EpollEchoServerPubSub $(LIB_FUZZING_ENGINE) *.o
# "Unit tests"
$(CXX) $(CXXFLAGS) -std=c++17 -O3 Extensions.cpp -o $(OUT)/Extensions $(LIB_FUZZING_ENGINE)
$(CXX) $(CXXFLAGS) -std=c++17 -O3 QueryParser.cpp -o $(OUT)/QueryParser $(LIB_FUZZING_ENGINE)
$(CXX) $(CXXFLAGS) -std=c++17 -O3 MultipartParser.cpp -o $(OUT)/MultipartParser $(LIB_FUZZING_ENGINE)
$(CXX) $(CXXFLAGS) -std=c++17 -O3 -I../uSockets/src WebSocket.cpp -o $(OUT)/WebSocket $(LIB_FUZZING_ENGINE)
$(CXX) $(CXXFLAGS) -std=c++17 -O3 Http.cpp -o $(OUT)/Http $(LIB_FUZZING_ENGINE)
$(CXX) $(CXXFLAGS) -DUWS_WITH_PROXY -std=c++17 -O3 Http.cpp -o $(OUT)/HttpWithProxy $(LIB_FUZZING_ENGINE)
$(CXX) $(CXXFLAGS) -DUWS_MOCK_ZLIB -std=c++17 -O3 PerMessageDeflate.cpp -o $(OUT)/PerMessageDeflate $(LIB_FUZZING_ENGINE)
$(CXX) $(CXXFLAGS) -std=c++17 -O3 TopicTree.cpp -o $(OUT)/TopicTree $(LIB_FUZZING_ENGINE)
regression_test:
$(OUT)/EpollEchoServer seed-corpus/EpollEchoServer/regressions/*
$(OUT)/EpollHelloWorld seed-corpus/EpollHelloWorld/regressions/*
$(OUT)/EpollEchoServerPubSub seed-corpus/EpollEchoServerPubSub/regressions/*
# $(OUT)/Extensions seed-corpus/Extensions/regressions/*
# $(OUT)/QueryParser seed-corpus/QueryParser/regressions/*
$(OUT)/TopicTree seed-corpus/TopicTree/regressions/*
$(OUT)/WebSocket seed-corpus/WebSocket/regressions/*
$(OUT)/Http seed-corpus/Http/regressions/*
$(OUT)/HttpWithProxy seed-corpus/HttpWithProxy/regressions/*
# $(OUT)/MultipartParser seed-corpus/MultipartParser/regressions/*
$(OUT)/PerMessageDeflate seed-corpus/PerMessageDeflate/regressions/*
================================================
FILE: fuzzing/MultipartParser.cpp
================================================
/* This is a fuzz test of the multipart parser */
#define WIN32_EXPORT
#include <cstdio>
#include <string>
#include <cstdlib>
#include "../src/Multipart.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (!size) {
return 0;
}
char *mutableMemory = (char *) malloc(size);
memcpy(mutableMemory, data, size);
/* First byte determines how long contentType is */
unsigned char contentTypeLength = data[0];
size--;
std::string_view contentType((char *) mutableMemory + 1, std::min<size_t>(contentTypeLength, size));
size -= contentType.length();
std::string_view body((char *) mutableMemory + 1 + contentType.length(), size);
uWS::MultipartParser mp(contentType);
if (mp.isValid()) {
mp.setBody(body);
std::pair<std::string_view, std::string_view> headers[10];
while (true) {
std::optional<std::string_view> optionalPart = mp.getNextPart(headers);
if (!optionalPart.has_value()) {
break;
}
std::string_view part = optionalPart.value();
for (int i = 0; headers[i].first.length(); i++) {
/* We care about content-type and content-disposition */
if (headers[i].first == "content-disposition") {
/* Parse the parameters */
uWS::ParameterParser pp(headers[i].second);
while (true) {
auto [key, value] = pp.getKeyValue();
if (!key.length()) {
break;
}
}
}
}
}
}
free(mutableMemory);
return 0;
}
================================================
FILE: fuzzing/PerMessageDeflate.cpp
================================================
/* This is a fuzz test of the permessage-deflate module */
#define WIN32_EXPORT
#include <cstdio>
#include <string>
#include <bitset>
/* We test the permessage deflate module */
#include "../src/PerMessageDeflate.h"
#include "helpers.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
/* First byte determines what compressor to use */
if (size >= 1) {
uWS::CompressOptions compressors[] = {
uWS::DEDICATED_COMPRESSOR_3KB,
uWS::DEDICATED_COMPRESSOR_4KB,
uWS::DEDICATED_COMPRESSOR_8KB,
uWS::DEDICATED_COMPRESSOR_16KB,
uWS::DEDICATED_COMPRESSOR_32KB,
uWS::DEDICATED_COMPRESSOR_64KB,
uWS::DEDICATED_COMPRESSOR_128KB,
uWS::DEDICATED_COMPRESSOR_256KB
};
auto compressor = compressors[data[0] % 8];
data++;
size--;
/* Bits 0 - 256 are okay */
std::bitset<257> b;
/* If we could specify LARGE_BUFFER_SIZE small here we could force it to inflate in chunks,
* triggering more line coverage. Currently it is set to 16kb which is always too much */
struct StaticData {
uWS::DeflationStream deflationStream;
uWS::InflationStream inflationStream;
uWS::ZlibContext zlibContext;
} staticData = {compressor, compressor};
/* Why is this padded? */
makeChunked(makePadded(data, size), size, [&staticData, &b](const uint8_t *data, size_t size) {
auto inflation = staticData.inflationStream.inflate(&staticData.zlibContext, std::string_view((char *) data, size), 256, true);
/* Trigger ASAN flaws if length is more than 256 */
if (inflation.has_value()) {
b.set(inflation->length());
}
});
makeChunked(makePadded(data, size), size, [&staticData](const uint8_t *data, size_t size) {
/* Always reset */
staticData.deflationStream.deflate(&staticData.zlibContext, std::string_view((char *) data, size), true);
});
}
return 0;
}
================================================
FILE: fuzzing/QueryParser.cpp
================================================
#include "../src/QueryParser.h"
#include <string>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
std::string modifiableInput((char *) data, size);
uWS::getDecodedQueryValue("", modifiableInput);
uWS::getDecodedQueryValue("hello", modifiableInput);
return 0;
}
================================================
FILE: fuzzing/QueryParser.dict
================================================
"?"
"%FF"
"&"
"+"
"hello"
"="
================================================
FILE: fuzzing/README.md
================================================
# Fuzz-testing of various parsers, mocked examples and system libraries
A secure web server must be capable of receiving mass amount of malicious input without misbehaving or performing illegal actions, such as stepping outside of a memory block or otherwise spilling the beans.
### Continuous fuzzing under various sanitizers is done as part of the [Google OSS-Fuzz](https://github.com/google/oss-fuzz#oss-fuzz---continuous-fuzzing-for-open-source-software) project:
* UndefinedBehaviorSanitizer
* AddressSanitizer
* MemorySanitizer
### Overall coverage is about 95% for both uSockets and uWebSockets, all source code included
* No defects or outstanding bugs
* No timeouts, OOM, crashes or other issues
* Transparent reporting of found issues: https://bugs.chromium.org/p/oss-fuzz/issues/list?q=label%3AProj-uwebsockets&can=1
### Currently the following parts are individually fuzzed:
* WebSocket handshake generator
* WebSocket message parser
* WebSocket extensions parser & negotiator
* WebSocket permessage-deflate compression/inflation helper
* Http parser (with and without Proxy Protocol v2)
* Http method/url router
* Pub/sub "topic tree"
### While some targets are entire (mocked) example apps
* libEpollFuzzer mocks the kernel syscalls and allows to cover a lot of uSockets source code.
* A mock implementation of uSockets allows to cover a lot of the inbetween logic of uWebSockets.
================================================
FILE: fuzzing/TopicTree.cpp
================================================
#define WIN32_EXPORT
#include "helpers.h"
/* Test for the topic tree */
#include "../src/TopicTree.h"
#include <memory>
// std::vector<std::string_view> topics = {"", "one", "two", "three"};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
/* Create topic tree */
uWS::TopicTree<std::string, std::string_view> topicTree([](uWS::Subscriber *s, std::string &message, auto flags) {
/* Depending on what publishing we do below (with or without empty strings),
* this assumption can hold true or not. For now it should hold true */
if (!message.length()) {
free((void *) -1);
}
/* Break if we have no subscriptions (not really an error, just to bring more randomness) */
if (s->topics.size() == 0) {
return true;
}
/* Success */
return false;
});
/* Holder for all manually allocated subscribers */
std::map<uint32_t, uWS::Subscriber *> subscribers;
/* Iterate the padded fuzz as chunks */
makeChunked(makePadded(data, size), size, [&topicTree, &subscribers](const uint8_t *data, size_t size) {
/* We need at least 5 bytes */
if (size > 4) {
/* Last of all is a string */
std::string_view lastString((char *) data + 5, size - 5);
/* Why not */
topicTree.lookupTopic(lastString);
/* First 4 bytes is the subscriber id */
uint32_t id;
memcpy(&id, data, 4);
/* Then one byte action */
if (data[4] == 'S') {
/* Some ridiculously long topics has to be cut short (OOM) */
if (lastString.length() > 512) {
lastString = "too long!";
}
/* Subscribe */
if (subscribers.find(id) == subscribers.end()) {
/* Limit number of subscribers to 100 (OOM) */
if (subscribers.size() > 100) {
return;
}
uWS::Subscriber *subscriber = topicTree.createSubscriber();
subscribers[id] = subscriber;
topicTree.subscribe(subscriber, lastString);
} else {
/* Limit per subscriber subscriptions (OOM) */
uWS::Subscriber *subscriber = subscribers[id];
if (subscriber->topics.size() < 50) {
topicTree.subscribe(subscriber, lastString);
}
}
} else if (data[4] == 'U') {
/* Unsubscribe */
auto it = subscribers.find(id);
if (it != subscribers.end()) {
topicTree.unsubscribe(it->second, lastString);
}
} else if (data[4] == 'F') {
/* Free subscriber */
auto it = subscribers.find(id);
if (it != subscribers.end()) {
topicTree.freeSubscriber(it->second);
subscribers.erase(it);
}
} else if (data[4] == 'A') {
/* Unsubscribe from all */
auto it = subscribers.find(id);
if (it != subscribers.end()) {
std::vector<std::string> topics;
for (auto *topic : it->second->topics) {
topics.push_back(topic->name);
}
for (std::string &topic : topics) {
topicTree.unsubscribe(it->second, topic);
}
}
} else if (data[4] == 'O') {
/* Drain one socket */
auto it = subscribers.find(id);
if (it != subscribers.end()) {
topicTree.drain(it->second);
}
} else if (data[4] == 'P') {
/* Publish only if we actually have data */
if (lastString.length()) {
topicTree.publish(nullptr, lastString, std::string(lastString));
} else {
/* We could use having more strings */
topicTree.publish(nullptr, "", "anything");
}
} else {
/* Drain for everything else (OOM) */
topicTree.drain();
}
}
});
/* Remove any subscriber from the tree */
for (auto &p : subscribers) {
topicTree.freeSubscriber(p.second);
}
return 0;
}
================================================
FILE: fuzzing/TopicTree.dict
================================================
"S"
"P"
"A"
"U"
"+"
"/"
"#"
"\x00\x00\x00\x00"
================================================
FILE: fuzzing/WebSocket.cpp
================================================
/* This is a fuzz test of the websocket parser */
#define WIN32_EXPORT
#include "helpers.h"
/* We test the websocket parser */
#include "../src/WebSocketProtocol.h"
struct Impl {
static bool refusePayloadLength(uint64_t length, uWS::WebSocketState<true> *wState, void *s) {
/* We need a limit */
if (length > 16000) {
return true;
}
/* Return ok */
return false;
}
static bool setCompressed(uWS::WebSocketState<true> *wState, void *s) {
/* We support it */
return true;
}
static void forceClose(uWS::WebSocketState<true> *wState, void *s, std::string_view reason = {}) {
}
static bool handleFragment(char *data, size_t length, unsigned int remainingBytes, int opCode, bool fin, uWS::WebSocketState<true> *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);
}
/* Return ok */
return false;
}
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
/* Create the parser state */
uWS::WebSocketState<true> state;
makeChunked(makePadded(data, size), size, [&state](const uint8_t *data, size_t size) {
/* Parse it */
uWS::WebSocketProtocol<true, Impl>::consume((char *) data, size, &state, nullptr);
});
return 0;
}
================================================
FILE: fuzzing/helpers.h
================================================
#ifndef HELPERS_H
#define HELPERS_H
/* Common helpers for fuzzing */
#include <functional>
#include <string_view>
#include <cstring>
/* We use this to pad the fuzz */
static inline const uint8_t *makePadded(const uint8_t *data, size_t size) {
static int paddedLength = 512 * 1024;
static char *padded = new char[128 + paddedLength + 128];
/* Increase landing area if required */
if (paddedLength < size) {
delete [] padded;
paddedLength = size;
padded = new char [128 + paddedLength + 128];
}
memcpy(padded + 128, data, size);
return (uint8_t *) padded + 128;
}
/* Splits the fuzz data in one or many chunks */
static inline void makeChunked(const uint8_t *data, size_t size, std::function<void(const uint8_t *data, size_t size)> cb) {
/* First byte determines chunk size; 0 is all that remains, 1-255 is small chunk */
for (int i = 0; i < size; ) {
unsigned int chunkSize = data[i++];
if (!chunkSize) {
chunkSize = size - i;
} else {
chunkSize = std::min<int>(chunkSize, size - i);
}
cb(data + i, chunkSize);
i += chunkSize;
}
}
/* Reads all bytes to trigger invalid reads */
static inline void readBytes(std::string_view s) {
volatile int sum = 0;
for (int i = 0; i < s.size(); i++) {
sum += s[i];
}
}
#endif
================================================
FILE: libEpollBenchmarker/Makefile
================================================
# You need to link with wrapped syscalls
override CFLAGS += -Wl,--wrap=recv,--wrap=bind,--wrap=listen,--wrap=send,--wrap=socket,--wrap=epoll_wait,--wrap=accept4,--wrap=epoll_ctl
# Include uSockets and uWebSockets
override CFLAGS += -DUWS_NO_ZLIB -I../src -I../uSockets/src
default:
make -C ../uSockets
$(CXX) -flto -O3 -std=c++17 ../examples/HelloWorld.cpp epoll_benchmarker.cpp $(CFLAGS) -o HelloWorld ../uSockets/uSockets.a
================================================
FILE: libEpollBenchmarker/epoll_benchmarker.cpp
================================================
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#ifdef __cplusplus
extern "C" {
#endif
#define NUM_SOCKETS 10
uint64_t listen_socket_epoll_data = 0;
epoll_event ready_events[NUM_SOCKETS] = {};
static int accepted_sockets = 0;
int __wrap_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) {
// the listen socket
if (fd == 500) {
listen_socket_epoll_data = event->data.u64;
return 0;
} else {
if (fd < 500) {
} else {
// on our FDs
ready_events[fd - 500 - 1].data.u64 = event->data.u64;
ready_events[fd - 500 - 1].events = EPOLLIN;
}
return 0;
}
}
int __wrap_epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout) {
if (accepted_sockets != NUM_SOCKETS) {
events[0].events = EPOLLIN;
events[0].data.u64 = listen_socket_epoll_data;
return 1;
} else {
for (int i = 0; i < NUM_SOCKETS; i++) {
events[i] = ready_events[i];
}
return NUM_SOCKETS;
}
}
int __wrap_recv(int sockfd, void *buf, size_t len, int flags) {
const char request[] =
"GET /joyent/http-parser HTTP/1.1\r\n"
"Host: github.com\r\n"
"DNT: 1\r\n"
"Accept-Encoding: gzip, deflate, sdch\r\n"
"Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\r\n"
"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/39.0.2171.65 Safari/537.36\r\n"
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,"
"image/webp,*/*;q=0.8\r\n"
"Referer: https://github.com/joyent/http-parser\r\n"
"Connection: keep-alive\r\n"
"Cache-Control: max-age=0\r\n\r\n";
memcpy(buf, request, sizeof(request) - 1);
return sizeof(request) - 1;
}
int __wrap_send(int sockfd, const void *buf, size_t len, int flags) {
static int sent = 0;
static clock_t lastTime = clock();
if (++sent == 1000000) {
// print how long it took to make 1 million requests
clock_t newTime = clock();
float elapsed = float(newTime - lastTime) / CLOCKS_PER_SEC;
printf("Req/sec: %f million\n", (1000000.0f / elapsed) / 1000000.0f);
sent = 0;
lastTime = newTime;
}
return len;
}
int __wrap_bind() {
return 0;
}
int __wrap_setsockopt() {
return 0;
}
int __wrap_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
if (accepted_sockets < NUM_SOCKETS) {
accepted_sockets++;
return accepted_sockets + 500;
} else {
return -1;
}
}
int __wrap_listen() {
return 0;
}
int __wrap_socket(int domain, int type, int protocol) {
return 500;
}
#ifdef __cplusplus
}
#endif
================================================
FILE: misc/READMORE.md
================================================
# µWebSockets v19 user manual
For a list of frequently asked questions you may use the Discussions tab here on GitHub. Please don't misuse the issue tracker as your personal Q&A, we use it only for bug reporting. Discussions tab is less strict about content.
## Motivation and goals
µWebSockets is a simple to use yet thoroughly optimized, standards compliant and secure implementation of WebSockets (and HTTP).
It comes with built-in pub/sub support, URL routing, TLS 1.3, SNI, IPv6, permessage-deflate and is battle tested as one of the most popular implementations, reaching many millions of end-users daily. It's currently juggling billions of USD in many popular Bitcoin exchanges, every day with outstanding real-world performance.
### Standards compliant
Unlike other "pub/sub brokers", µWS does not assume or push any particular application protocol but only operates over raw, standard WebSockets. You need nothing but a standards compliant web browser and a handful of standards compliant JavaScript to communicate with it. No particular client library is needed or enforced - this unlike inadequate solutions like Socket.IO where you end up locked to a set of proprietary non-standard protocols with horrible performance.
### Performant
The implementation is header-only C++17 (but examples use C++20 features for brevity and elegance!), cross-platform and compiles down to a tiny binary of a handful kilobytes.
It depends on µSockets, which is a standard C project for Linux, macOS & Windows.
Performance wise you can expect to outperform, or equal, just about anything similar out there, that's the fundamental goal of the project. I can show small-message cases where µWS **with SSL** significantly outperforms the fastest Golang servers running **non-SSL**. You get the SSL for free in a sense (shown to be true for messaging with up to 4 kB per message).
We've [openly presented](https://medium.com/swlh/100k-secure-websockets-with-raspberry-pi-4-1ba5d2127a23) detailed cases where a single Raspberry Pi 4 can serve more than 100k very active TLS 1.3 WebSockets, simultaneously, with excellent stability. This is entirely impossible with the vast majority of alternative solutions. Most solutions cramp up and become unreliable at a tiny fraction of this load, on such a limited hardware. We also have measurements where we [serve 12x the HTTP requests per second](https://levelup.gitconnected.com/serving-100k-requests-second-from-a-fanless-raspberry-pi-4-over-ethernet-fdd2c2e05a1e) as compared to Node.js.
### Simple to use
Another goal of the project is minimalism, simplicity and elegance.
Design wise it follows an ExpressJS-like interface where you attach callbacks to different URL routes.
This way you can easily build complete REST/WebSocket services in a few lines of code.
Boilerplate logic like heartbeat timeouts, backpressure handling, ping/pong and other annoyances are handled efficiently and easily by the library itself. You write business logic, the library handles the protocol(s).
The project is async only and runs local to one thread. You scale it as individual threads much like Node.js scales as individual processes. That is, the implementation only sees a single thread and is not thread-safe. There are simple ways to do threading via async delegates though, if you really need to.
## Compiling
µWebSockets is 100% standard header-only C++17 - it compiles on any platform. However, it depends on µSockets in all cases, which is platform-specific C code that runs on Linux, Windows and macOS.
There are a few compilation flags for µSockets (see its documentation), but common between µSockets and µWebSockets flags are as follows:
* LIBUS_NO_SSL - disable OpenSSL dependency/functionality for uSockets and uWebSockets builds
* UWS_NO_ZLIB - disable Zlib dependency/functionality for uWebSockets
You can use the Makefile on Linux and macOS. It is simple to use and builds the examples for you. `WITH_OPENSSL=1 make` builds all examples with SSL enabled. Examples will fail to listen if cert and key cannot be found, so make sure to specify a path that works for you.
## User manual
### uWS::App & uWS::SSLApp
You begin your journey by constructing an "App". Either an SSL-app or a regular TCP-only App. The uWS::SSLApp constructor takes a struct holding SSL options such as cert and key. Interfaces for both apps are identical, so let's call them both "App" from now on.
Apps follow the builder pattern, member functions return the App so that you can chain calls.
### App.get, post, put, [...] and any routes
You attach behavior to "URL routes". A lambda is paired with a "method" (Http method that is) and a pattern (the URL matching pattern).
Methods are many, but most common are probably get & post. They all have the same signature, let's look at one example:
```c++
uWS::App().get("/hello", [](auto *res, auto *req) {
res->end("Hello World!");
});
```
Important for all routes is that "req", the `uWS::HttpRequest *` dies with return. In other words, req is stack allocated so don't keep it in your pocket.
res, the `uWS::HttpResponse<SSL> *` will be alive and accessible until either its .onAborted callback emits, or you've responded to the request via res.end or res.tryEnd.
In other words, you either respond to the request immediately and return, or you attach lambdas to the res (which may hold captured data), and respond later on in some other async callback.
Data that you capture in a res follows RAII and is move-only so you can properly move-in for instance std::string buffers that you may use to, for instance, buffer upp streaming POST data. It's pretty cool, check out mutable lambdas with move-only captures.
The "any" route will match any method.
#### Pattern matching
Routes are matched in **order of specificity**, not by the order you register them:
* Highest priority - static routes, think "/hello/this/is/static".
* Middle priority - parameter routes, think "/candy/:kind", where value of :kind is retrieved by req.getParameter(0).
* Lowest priority - wildcard routes, think "/hello/*".
In other words, the more specific a route is, the earlier it will match. This allows you to define wildcard routes that match a wide range of URLs and then "carve" out more specific behavior from that.
"Any" routes, those who match any HTTP method, will match with lower priority than routes which specify their specific HTTP method (such as GET) if and only if the two routes otherwise are equally specific.
#### Middlewares
A very commonly asked question is how to achieve something like middlewares. We don't support middlewares as something built into the router itself. Partly because routes cannot pass data to other routes, partly because the HttpRequest object being stack-allocated and only valid in one single callback invocation, but most importantly - you can **easily** achieve the same function-chaining that is middlewares by instead using simple high-order functions and functional programming. There are tons of examples of this under Discussions (since it is a commonly asked question). A middleware isn't really something that has to be built-in to the server library itself, it really is just **a regular function**. By passing functions to other functions you can build chains of behaviors in very elegant and efficient ways.
Whether this library should keep a set of commonly used functions is another question - we might do that in the future and we might add an example of its usage but right now there is nothing like this provided. We aim to provide an easy to use server implementation that you can build things on. Not complete business logic puzzle pieces.
#### Streaming data
You should never call res.end(huge buffer). res.end guarantees sending so backpressure will probably spike. Instead you should use res.tryEnd to stream huge data part by part. Use in combination with res.onWritable and res.onAborted callbacks.
Tip: Check out the JavaScript project, it has many useful examples of async streaming of huge data.
#### Corking
It is very important to understand the corking mechanism, as that is responsible for efficiently formatting, packing and sending data. Without corking your app will still work reliably, but can perform very bad and use excessive networking. In some cases the performance can be dreadful without proper corking.
That's why your sockets will be corked by default in most simple cases, including all of the examples provided. However there are cases where default corking cannot happen automatically.
* Whenever your registered business logic (your callbacks) are called from the library, such as when receiving a message or when a socket opens, you'll be corked by default. Whatever you do with the socket inside of that callback will be efficient and properly corked.
* If you have callbacks registered to some other library, say libhiredis, those callbacks will not be called with corked sockets (how could **we** know when to cork the socket if we don't control the third-party library!?).
* Only one single socket can be corked at any point in time (isolated per thread, of course). It is efficient to cork-and-uncork.
* Whenever your callback is a coroutine, such as the JavaScript async/await, automatic corking can only happen in the very first portion of the coroutine (consider await a separator which essentially cuts the coroutine into smaller segments). Only the first "segment" of the coroutine will be called from µWS, the following async segments will be called by the JavaScript runtime at a later point in time and will thus not be under our control with default corking enabled.
* Corking is important even for calls which seem to be "atomic" and only send one chunk. res->end, res->tryEnd, res->writeStatus, res->writeHeader will most likely send multiple chunks of data and is very important to properly cork.
You can make sure corking is enabled, even for cases where default corking would be enabled, by wrapping whatever sending function calls in a lambda like so:
```c++
res->cork([res]() {
res->end("This Http response will be properly corked and efficient in all cases");
});
```
The above res->end call will actually call three separate send functions; res->writeStatus, res->writeHeader and whatever it does itself. By wrapping the call in res->cork you make sure these three send functions are efficient and only result in one single send syscall and one single SSL block if using SSL.
Keep this in mind, corking is by far the single most important performance trick to use. Even when streaming huge amounts of data it can be useful to cork. At least in the very tip of the response, as that holds the headers and status.
### The App.ws route
WebSocket "routes" are registered similarly, but not identically.
Every websocket route has the same pattern and pattern matching as for Http, but instead of one single callback you have a whole set of them, here's an example:
```c++
uWS::App().ws<PerSocketData>("/*", {
/* Settings */
.compression = uWS::SHARED_COMPRESSOR,
.maxPayloadLength = 16 * 1024,
.idleTimeout = 10,
/* Handlers */
.upgrade = [](auto *res, auto *req, auto *context) {
/* You may read from req only here, and COPY whatever you need into your PerSocketData.
* See UpgradeSync and UpgradeAsync examples. */
},
.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 message) {
},
.pong = [](auto *ws, std::string_view message) {
},
.close = [](auto *ws, int code, std::string_view message) {
}
});
```
WebSocket routes specify a user data type that should be used to keep per-websocket data. Many times people tend to attach user data
which should belong to the websocket by putting the pointer and the user data in a std::map. That's wrong! Don't do that!
#### Use the WebSocket.getUserData() feature
You should use the provided user data feature to store and attach any per-socket user data. Going from user data to WebSocket is possible if you make your user data hold a pointer to WebSocket, and hook things up in the WebSocket open handler. Your user data memory is valid while your WebSocket is.
If you want to create something more elaborate you could have the user data hold a pointer to some dynamically allocated memory block that keeps a boolean whether the WebSocket is still valid or not. Sky is the limit here, you should never need any std::map for this.
#### WebSockets are valid from open to close
All given WebSocket pointers are guaranteed to live from open event (where you got your WebSocket) until close event is called. So is the user data memory. One open event will always end in exactly one close event, they are 1-to-1 and will always be balanced no matter what. Use them to drive your RAII data types, they can be seen as constructor and destructor.
Message events will never emit outside of open/close. Calling WebSocket.close or WebSocket.end will immediately call the close handler.
#### Backpressure in websockets
Similarly to for Http, methods such as ws.send(...) can cause backpressure. Make sure to check ws.getBufferedAmount() before sending, and check the return value of ws.send before sending any more data. WebSockets do no
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
SYMBOL INDEX (346 symbols across 89 files)
FILE: autobahn/server-test.js
constant AUTOBAHN_TESTSUITE_DOCKER (line 7) | const AUTOBAHN_TESTSUITE_DOCKER =
function failed (line 15) | function failed(name) {
FILE: benchmarks/broadcast_test.c
type http_socket (line 33) | struct http_socket {
function noop (line 52) | void noop(struct us_loop_t *loop) {
function start_iteration (line 56) | void start_iteration() {
function next_connection (line 65) | void next_connection(struct us_socket_t *s) {
type us_socket_t (line 80) | struct us_socket_t
type us_socket_t (line 80) | struct us_socket_t
type http_socket (line 81) | struct http_socket
type http_socket (line 81) | struct http_socket
type us_socket_t (line 94) | struct us_socket_t
type us_socket_t (line 94) | struct us_socket_t
type us_socket_t (line 102) | struct us_socket_t
type us_socket_t (line 102) | struct us_socket_t
type us_socket_t (line 106) | struct us_socket_t
type us_socket_t (line 106) | struct us_socket_t
type http_socket (line 108) | struct http_socket
type http_socket (line 108) | struct http_socket
type us_socket_t (line 136) | struct us_socket_t
type us_socket_t (line 136) | struct us_socket_t
type http_socket (line 137) | struct http_socket
type http_socket (line 137) | struct http_socket
type us_socket_t (line 150) | struct us_socket_t
type us_socket_t (line 150) | struct us_socket_t
function main (line 160) | int main(int argc, char **argv) {
FILE: benchmarks/load_test.c
type SocketState (line 30) | typedef struct {
type BenchmarkConfig (line 38) | typedef struct {
function init_text_request (line 65) | void init_text_request(int size, BenchmarkConfig *config) {
function init_deflated_request (line 109) | void init_deflated_request(int size, BenchmarkConfig *config) {
function next_connection (line 193) | void next_connection(struct us_socket_t *s) {
type us_socket_t (line 203) | struct us_socket_t
type us_socket_t (line 203) | struct us_socket_t
type us_socket_t (line 217) | struct us_socket_t
type us_socket_t (line 217) | struct us_socket_t
type us_socket_t (line 223) | struct us_socket_t
type us_socket_t (line 223) | struct us_socket_t
type us_socket_t (line 228) | struct us_socket_t
type us_socket_t (line 228) | struct us_socket_t
type us_socket_t (line 252) | struct us_socket_t
type us_socket_t (line 252) | struct us_socket_t
type us_socket_t (line 264) | struct us_socket_t
type us_socket_t (line 264) | struct us_socket_t
function on_wakeup (line 272) | void on_wakeup(struct us_loop_t *loop) {}
function on_pre (line 273) | void on_pre(struct us_loop_t *loop) {}
function on_post (line 274) | void on_post(struct us_loop_t *loop) {}
function main (line 282) | int main(int argc, char **argv) {
FILE: benchmarks/parser.cpp
type Impl (line 10) | struct Impl {
method refusePayloadLength (line 11) | static bool refusePayloadLength(uint64_t length, uWS::WebSocketState<t...
method setCompressed (line 22) | static bool setCompressed(uWS::WebSocketState<true> *wState, void *s) {
method forceClose (line 27) | static void forceClose(uWS::WebSocketState<true> *wState, void *s, std...
method handleFragment (line 31) | static bool handleFragment(char *data, size_t length, unsigned int rem...
function init_medium_message (line 54) | void init_medium_message(unsigned int size) {
function main (line 74) | int main() {
FILE: benchmarks/scale_test.c
type http_socket (line 42) | struct http_socket {
type us_socket_t (line 50) | struct us_socket_t
function on_pre (line 53) | void on_pre(struct us_loop_t *loop) {
function on_post (line 58) | void on_post(struct us_loop_t *loop) {
function next_connection (line 62) | void next_connection(struct us_socket_t *s) {
function on_wakeup (line 79) | void on_wakeup(struct us_loop_t *loop) {
type us_socket_t (line 87) | struct us_socket_t
type us_socket_t (line 87) | struct us_socket_t
type http_socket (line 88) | struct http_socket
type http_socket (line 88) | struct http_socket
type us_socket_t (line 113) | struct us_socket_t
type us_socket_t (line 113) | struct us_socket_t
type us_socket_t (line 123) | struct us_socket_t
type us_socket_t (line 123) | struct us_socket_t
type us_socket_t (line 128) | struct us_socket_t
type us_socket_t (line 128) | struct us_socket_t
type timespec (line 135) | struct timespec
type us_socket_t (line 173) | struct us_socket_t
type us_socket_t (line 173) | struct us_socket_t
type http_socket (line 174) | struct http_socket
type http_socket (line 174) | struct http_socket
type us_socket_t (line 195) | struct us_socket_t
type us_socket_t (line 195) | struct us_socket_t
type http_socket (line 196) | struct http_socket
type http_socket (line 196) | struct http_socket
type us_socket_t (line 208) | struct us_socket_t
type us_socket_t (line 208) | struct us_socket_t
function main (line 217) | int main(int argc, char **argv) {
FILE: build.c
function main (line 3) | int main(int argc, char **argv) {
FILE: build.h
function env_is (line 7) | int env_is(char *env, char *target) {
function run (line 20) | int run(const char *cmd, ...) {
FILE: examples/Broadcast.cpp
function main (line 11) | int main() {
FILE: examples/BroadcastingEchoServer.cpp
type us_listen_socket_t (line 3) | struct us_listen_socket_t
function main (line 5) | int main() {
FILE: examples/CachingApp.cpp
function main (line 4) | int main() {
FILE: examples/Client.cpp
function main (line 7) | int main() {
FILE: examples/Crc32.cpp
function crc32 (line 18) | uint32_t crc32(const char *s, size_t n, uint32_t crc = 0xFFFFFFFF) {
function main (line 33) | int main() {
FILE: examples/EchoBody.cpp
function main (line 6) | int main() {
FILE: examples/EchoServer.cpp
function main (line 7) | int main() {
FILE: examples/EchoServerThreaded.cpp
function main (line 5) | int main() {
FILE: examples/HelloWorld.cpp
function main (line 5) | int main() {
FILE: examples/HelloWorldThreaded.cpp
function main (line 4) | int main() {
FILE: examples/Http3Server.cpp
function main (line 10) | int main() {
function main (line 96) | int main() {
FILE: examples/HttpServer.cpp
function main (line 14) | int main(int argc, char **argv) {
FILE: examples/ParameterRoutes.cpp
function main (line 5) | int main() {
FILE: examples/Precompress.cpp
function main (line 3) | int main() {
FILE: examples/SecureGzipFileServer.cpp
type StringViewHasher (line 25) | struct StringViewHasher {
type StringViewEqual (line 32) | struct StringViewEqual {
function load_file_content (line 46) | std::pair<std::string, bool> load_file_content(const std::filesystem::pa...
function load_files (line 86) | void load_files(const std::string& root, int inotify_fd = -1) {
function inotify_reloader_function (line 116) | void inotify_reloader_function(const std::string& root, int inotify_fd) {
function main (line 133) | int main(int argc, char** argv) {
FILE: examples/ServerName.cpp
function main (line 5) | int main() {
FILE: examples/SmokeTest.cpp
type Stream (line 5) | struct Stream {
function streamData (line 12) | void streamData(auto *res, auto stream, int chunk) {
function main (line 37) | int main() {
FILE: examples/UpgradeAsync.cpp
function main (line 7) | int main() {
FILE: examples/UpgradeSync.cpp
function main (line 7) | int main() {
FILE: examples/helpers/AsyncFileReader.h
function peek (line 11) | struct AsyncFileReader {
function request (line 73) | void request(int offset, std::function<void(std::string_view)> cb) {
function abort (line 123) | void abort() {
function getFileSize (line 127) | int getFileSize() {
FILE: examples/helpers/AsyncFileStreamer.h
function updateRootCache (line 3) | struct AsyncFileStreamer {
FILE: examples/helpers/Middleware.h
function hasExt (line 2) | inline bool hasExt(std::string_view file, std::string_view ext) {
FILE: examples/helpers/optparse.h
type optparse (line 58) | struct optparse {
type optparse_argtype (line 68) | enum optparse_argtype {
type optparse_long (line 74) | struct optparse_long {
type optparse (line 84) | struct optparse
type optparse (line 96) | struct optparse
type optparse (line 105) | struct optparse
type optparse_long (line 106) | struct optparse_long
type optparse (line 119) | struct optparse
function optparse_error (line 128) | static int
function OPTPARSE_API (line 144) | OPTPARSE_API
function optparse_is_dashdash (line 156) | static int
function optparse_is_shortopt (line 162) | static int
function optparse_is_longopt (line 168) | static int
function optparse_permute (line 174) | static void
function optparse_argtype (line 184) | static int
function OPTPARSE_API (line 198) | OPTPARSE_API
function OPTPARSE_API (line 270) | OPTPARSE_API
function optparse_longopts_end (line 281) | static int
function optparse_from_long (line 287) | static void
function optparse_longopts_match (line 304) | static int
function optparse_long_fallback (line 327) | static int
function OPTPARSE_API (line 348) | OPTPARSE_API
FILE: fuzzing/AsyncEpollHelloWorld.cpp
type us_listen_socket_t (line 7) | struct us_listen_socket_t
function test (line 10) | void test() {
function teardown (line 45) | void teardown() {
FILE: fuzzing/EpollEchoServer.cpp
type us_listen_socket_t (line 7) | struct us_listen_socket_t
function test (line 10) | void test() {
function teardown (line 142) | void teardown() {
FILE: fuzzing/EpollEchoServerPubSub.cpp
type us_listen_socket_t (line 8) | struct us_listen_socket_t
function test (line 11) | void test() {
function teardown (line 84) | void teardown() {
FILE: fuzzing/EpollHelloWorld.cpp
type us_listen_socket_t (line 7) | struct us_listen_socket_t
type us_socket_t (line 8) | struct us_socket_t
function test (line 11) | void test() {
function teardown (line 159) | void teardown() {
FILE: fuzzing/Extensions.cpp
function LLVMFuzzerTestOneInput (line 12) | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FILE: fuzzing/Http.cpp
type StaticData (line 16) | struct StaticData {
type RouterData (line 18) | struct RouterData {
method StaticData (line 24) | StaticData() {
function LLVMFuzzerTestOneInput (line 76) | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FILE: fuzzing/MultipartParser.cpp
function LLVMFuzzerTestOneInput (line 11) | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FILE: fuzzing/PerMessageDeflate.cpp
function LLVMFuzzerTestOneInput (line 14) | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FILE: fuzzing/QueryParser.cpp
function LLVMFuzzerTestOneInput (line 5) | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FILE: fuzzing/TopicTree.cpp
function LLVMFuzzerTestOneInput (line 12) | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FILE: fuzzing/WebSocket.cpp
type Impl (line 10) | struct Impl {
method refusePayloadLength (line 11) | static bool refusePayloadLength(uint64_t length, uWS::WebSocketState<t...
method setCompressed (line 22) | static bool setCompressed(uWS::WebSocketState<true> *wState, void *s) {
method forceClose (line 27) | static void forceClose(uWS::WebSocketState<true> *wState, void *s, std...
method handleFragment (line 31) | static bool handleFragment(char *data, size_t length, unsigned int rem...
function LLVMFuzzerTestOneInput (line 47) | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FILE: fuzzing/helpers.h
function makeChunked (line 28) | static inline void makeChunked(const uint8_t *data, size_t size, std::fu...
function readBytes (line 44) | static inline void readBytes(std::string_view s) {
FILE: libEpollBenchmarker/epoll_benchmarker.cpp
function __wrap_epoll_ctl (line 23) | int __wrap_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) {
function __wrap_epoll_wait (line 42) | int __wrap_epoll_wait(int epfd, struct epoll_event *events,
function __wrap_recv (line 57) | int __wrap_recv(int sockfd, void *buf, size_t len, int flags) {
function __wrap_send (line 77) | int __wrap_send(int sockfd, const void *buf, size_t len, int flags) {
function __wrap_bind (line 92) | int __wrap_bind() {
function __wrap_setsockopt (line 96) | int __wrap_setsockopt() {
function __wrap_accept4 (line 100) | int __wrap_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
function __wrap_listen (line 109) | int __wrap_listen() {
function __wrap_socket (line 113) | int __wrap_socket(int domain, int type, int protocol) {
FILE: src/App.h
function namespace (line 27) | namespace uWS {
function namespace (line 60) | namespace uWS {
function constructorFailed (line 251) | bool constructorFailed() {
type us_socket_context_t (line 285) | struct us_socket_context_t
type us_socket_context_t (line 287) | struct us_socket_context_t
type us_socket_context_t (line 440) | struct us_socket_context_t
type us_socket_context_t (line 451) | struct us_socket_context_t
type us_socket_context_t (line 470) | struct us_socket_context_t
function Loop (line 663) | Loop *getLoop() {
function namespace (line 671) | namespace uWS {
FILE: src/AsyncSocket.h
function namespace (line 35) | namespace uWS {
FILE: src/AsyncSocketData.h
function namespace (line 23) | namespace uWS {
FILE: src/BloomFilter.h
function namespace (line 28) | namespace uWS {
FILE: src/CachingApp.h
function namespace (line 10) | namespace uWS {
type std (line 54) | typedef std::unordered_map<std::string_view, CachingHttpResponse *,
FILE: src/ChunkedEncoding.h
function namespace (line 31) | namespace uWS {
function ChunkIterator (line 195) | struct ChunkIterator {
function ChunkIterator (line 210) | ChunkIterator begin() {
function ChunkIterator (line 214) | ChunkIterator end() {
FILE: src/ClientApp.h
function namespace (line 5) | namespace uWS {
FILE: src/Http3App.h
function namespace (line 7) | namespace uWS {
FILE: src/Http3Context.h
function namespace (line 9) | namespace uWS {
function us_quic_listen_socket_t (line 110) | us_quic_listen_socket_t *listen(const char *host, int port) {
function init (line 119) | void init() {
function onHttp (line 133) | void onHttp(std::string method, std::string path, MoveOnlyFunction<void(...
FILE: src/Http3ContextData.h
type Http3Response (line 3) | struct Http3Response
type Http3Request (line 4) | struct Http3Request
function namespace (line 6) | namespace uWS {
FILE: src/Http3Request.h
function namespace (line 5) | namespace uWS {
FILE: src/Http3Response.h
function namespace (line 7) | namespace uWS {
FILE: src/Http3ResponseData.h
function namespace (line 8) | namespace uWS {
FILE: src/HttpContext.h
function namespace (line 33) | namespace uWS {
function free (line 428) | void free() {
function filter (line 437) | void filter(MoveOnlyFunction<void(HttpResponse<SSL> *, int)> &&filterHan...
function us_listen_socket_t (line 500) | us_listen_socket_t *listen(const char *host, int port, int options) {
function us_listen_socket_t (line 505) | us_listen_socket_t *listen(const char *path, int options) {
function onPreOpen (line 509) | void onPreOpen(LIBUS_SOCKET_DESCRIPTOR (*handler)(struct us_socket_conte...
function us_socket_t (line 514) | us_socket_t *adoptAcceptedSocket(LIBUS_SOCKET_DESCRIPTOR accepted_fd) {
FILE: src/HttpContextData.h
function namespace (line 26) | namespace uWS {
FILE: src/HttpErrors.h
function namespace (line 23) | namespace uWS {
FILE: src/HttpParser.h
function namespace (line 39) | namespace uWS {
function setParameters (line 166) | void setParameters(std::pair<int, std::string_view *> parameters) {
function setParameterOffsets (line 170) | void setParameterOffsets(std::map<std::string, unsigned short, std::less...
type HttpParser (line 195) | struct HttpParser {
function hasLess (line 220) | static inline uint64_t hasLess(uint64_t x, uint64_t n) {
function hasMore (line 224) | static inline uint64_t hasMore(uint64_t x, uint64_t n) {
function hasBetween (line 228) | static inline uint64_t hasBetween(uint64_t x, uint64_t m, uint64_t n) {
function notFieldNameWord (line 232) | static inline bool notFieldNameWord(uint64_t x) {
function isUnlikelyFieldNameByte (line 242) | static inline bool isUnlikelyFieldNameByte(unsigned char c)
function isFieldNameByteFastLowercased (line 249) | static inline bool isFieldNameByteFastLowercased(unsigned char &in) {
function getHeaders (line 356) | static unsigned int getHeaders(char *postPaddedBuffer, char *end, struct...
function else (line 666) | else if (fallback.length()) {
FILE: src/HttpResponse.h
function namespace (line 38) | namespace uWS {
function getProxiedRemotePort (line 236) | unsigned int getProxiedRemotePort() {
type us_socket_t (line 314) | struct us_socket_t
function HttpResponse (line 369) | HttpResponse *pause() {
function HttpResponse (line 375) | HttpResponse *resume() {
function HttpResponse (line 385) | HttpResponse *writeContinue() {
function write (line 452) | bool write(std::string_view data) {
function uintmax_t (line 485) | uintmax_t getWriteOffset() {
function maxRemainingBodyLength (line 492) | uint64_t maxRemainingBodyLength() {
function overrideWriteOffset (line 499) | void overrideWriteOffset(uintmax_t offset) {
function hasResponded (line 506) | bool hasResponded() {
FILE: src/HttpResponseData.h
function namespace (line 29) | namespace uWS {
FILE: src/HttpRouter.h
function namespace (line 34) | namespace uWS {
function setUrl (line 124) | inline void setUrl(std::string_view url) {
function executeHandlers (line 168) | bool executeHandlers(Node *parent, int urlSegment, USERDATA &userData) {
function route (line 257) | bool route(std::string_view method, std::string_view url) {
function cullNode (line 322) | bool cullNode(Node *parent, Node *node, uint32_t handler) {
function remove (line 362) | bool remove(std::string method, std::string pattern, uint32_t priority) {
FILE: src/LocalCluster.h
function namespace (line 13) | namespace uWS {
FILE: src/Loop.h
function namespace (line 27) | namespace uWS {
function preCb (line 55) | static void preCb(us_loop_t *loop) {
function postCb (line 63) | static void postCb(us_loop_t *loop) {
function Loop (line 80) | Loop *init() {
function Loop (line 85) | static Loop *create(void *hint) {
function LoopCleaner (line 112) | static LoopCleaner &getLazyLoop() {
function free (line 162) | void free() {
function addPostHandler (line 176) | void addPostHandler(void *key, MoveOnlyFunction<void(Loop *)> &&handler) {
function removePostHandler (line 183) | void removePostHandler(void *key) {
function addPreHandler (line 189) | void addPreHandler(void *key, MoveOnlyFunction<void(Loop *)> &&handler) {
function removePreHandler (line 196) | void removePreHandler(void *key) {
function defer (line 203) | void defer(MoveOnlyFunction<void()> &&cb) {
function run (line 215) | void run() {
function integrate (line 221) | void integrate() {
function setSilent (line 226) | void setSilent(bool silent) {
function run (line 232) | inline void run() {
FILE: src/LoopData.h
type us_timer_t (line 32) | struct us_timer_t
function namespace (line 34) | namespace uWS {
FILE: src/MessageParser.h
function namespace (line 30) | namespace uWS {
FILE: src/MoveOnlyFunction.h
function namespace (line 84) | namespace ofats {
function swap (line 211) | void swap(any_invocable_impl& rhs) noexcept {
function destroy (line 242) | void destroy() noexcept {
function R (line 249) | R call(ArgTypes... args) noexcept(is_noexcept) {
FILE: src/Multipart.h
function namespace (line 32) | namespace uWS {
type MultipartParser (line 122) | struct MultipartParser {
function isValid (line 162) | bool isValid() {
function setBody (line 167) | void setBody(std::string_view body) {
FILE: src/PerMessageDeflate.h
function namespace (line 27) | namespace uWS {
function namespace (line 77) | namespace uWS {
FILE: src/ProxyParser.h
function namespace (line 25) | namespace uWS {
FILE: src/QueryParser.h
function namespace (line 25) | namespace uWS {
FILE: src/TopicTree.h
function namespace (line 35) | namespace uWS {
FILE: src/Utilities.h
function namespace (line 25) | namespace uWS {
FILE: src/WebSocket.h
function namespace (line 28) | namespace uWS {
function hasNegotiatedCompression (line 99) | bool hasNegotiatedCompression() {
function SendStatus (line 105) | SendStatus sendPrepared(PreparedMessage &preparedMessage) {
FILE: src/WebSocketContext.h
type TemplatedApp (line 30) | struct TemplatedApp
type WebSocketProtocol (line 31) | struct WebSocketProtocol
function us_socket_context_t (line 35) | us_socket_context_t *getSocketContext() {
function setCompressed (line 44) | static bool setCompressed(WebSocketState<isServer> */*wState*/, void *s) {
function handleFragment (line 60) | static bool handleFragment(char *data, size_t length, unsigned int remai...
function refusePayloadLength (line 241) | static bool refusePayloadLength(uint64_t length, WebSocketState<isServer...
function free (line 413) | void free() {
FILE: src/WebSocketContextData.h
function namespace (line 32) | namespace uWS {
FILE: src/WebSocketData.h
function namespace (line 28) | namespace uWS {
FILE: src/WebSocketExtensions.h
function namespace (line 36) | namespace uWS {
FILE: src/WebSocketHandshake.h
function namespace (line 24) | namespace uWS {
FILE: src/WebSocketProtocol.h
function namespace (line 32) | namespace uWS {
function namespace (line 90) | namespace protocol {
function isValidUtf8 (line 122) | static bool isValidUtf8(unsigned char *s, size_t length)
function isValidUtf8 (line 133) | static bool isValidUtf8(unsigned char *s, size_t length)
type CloseFrame (line 177) | struct CloseFrame {
function CloseFrame (line 183) | static inline CloseFrame parseClosePayload(char *src, size_t length) {
function formatClosePayload (line 198) | static inline size_t formatClosePayload(char *dst, uint16_t code, const ...
function messageFrameSize (line 212) | static inline size_t messageFrameSize(size_t messageSize) {
function formatMessage (line 228) | inline size_t formatMessage(char *dst, const char *src, size_t length, O...
type WebSocketProtocol (line 282) | struct WebSocketProtocol {
function getOpCode (line 290) | char getOpCode(char *frame) {return *((unsigned char *) frame) & 15;}
function payloadLength (line 291) | static inline unsigned char payloadLength(char *frame) {return ((unsigne...
function rsv23 (line 292) | static inline bool rsv23(char *frame) {return *((unsigned char *) frame)...
function rsv1 (line 293) | static inline bool rsv1(char *frame) {return *((unsigned char *) frame) ...
function UnrolledXor (line 296) | inline void UnrolledXor(char * __restrict data, char * __restrict mask) {
function unmaskImprecise8 (line 304) | inline void unmaskImprecise8(char *src, uint64_t mask, unsigned int leng...
function unmaskImprecise4 (line 316) | inline void unmaskImprecise4(char *src, uint32_t mask, unsigned int leng...
function unmaskImpreciseCopyMask (line 327) | inline void unmaskImpreciseCopyMask(char *src, unsigned int length) {
function rotateMask (line 341) | static inline void rotateMask(unsigned int offset, char *mask) {
function unmaskInplace (line 349) | static inline void unmaskInplace(char *data, char *stop, char *mask) {
function consumeMessage (line 359) | static inline bool consumeMessage(T payLength, char *&src, unsigned int ...
function unmaskAll (line 418) | static inline void unmaskAll(char * __restrict data, char * __restrict m...
function consumeContinuation (line 424) | static inline bool consumeContinuation(char *&src, unsigned int &length,...
function consume (line 477) | static inline void consume(char *src, unsigned int length, WebSocketStat...
FILE: tests/BloomFilter.cpp
function main (line 55) | int main() {
FILE: tests/ChunkedEncoding.cpp
function consumeChunkEncoding (line 10) | void consumeChunkEncoding(int maxConsume, std::string_view &chunkEncoded...
function runBetterTest (line 58) | void runBetterTest(unsigned int maxConsume) {
function runTest (line 103) | void runTest(unsigned int maxConsume) {
function testWithoutTrailer (line 187) | void testWithoutTrailer() {
function main (line 214) | int main() {
FILE: tests/ExtensionsNegotiator.cpp
function testNegotiation (line 11) | void testNegotiation(bool wantCompression, int wantedCompressionWindow, ...
function main (line 24) | int main() {
FILE: tests/HttpParser.cpp
function main (line 6) | int main() {
FILE: tests/HttpRouter.cpp
function testMethodPriority (line 6) | void testMethodPriority() {
function testDeepParameterRoutes (line 49) | void testDeepParameterRoutes() {
function testPatternPriority (line 77) | void testPatternPriority() {
function testUpgrade (line 126) | void testUpgrade() {
function testBugReports (line 157) | void testBugReports() {
function testParameters (line 378) | void testParameters() {
function testPerformance (line 444) | void testPerformance() {
function main (line 467) | int main() {
FILE: tests/Query.cpp
function main (line 6) | int main() {
FILE: tests/TopicTree.cpp
function testCorrectness (line 13) | void testCorrectness() {
function testBugReport (line 88) | void testBugReport() {
function testReorderingv19 (line 149) | void testReorderingv19() {
function main (line 195) | int main() {
FILE: tests/smoke.mjs
function chunkedCrc32Test (line 21) | async function chunkedCrc32Test(array) {
function fixedCrc32Test (line 52) | async function fixedCrc32Test(array) {
Condensed preview — 115 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (605K chars).
[
{
"path": ".github/workflows/codeql.yml",
"chars": 2430,
"preview": "name: \"CodeQL\"\n\non:\n push:\n branches: [ 'master' ]\n pull_request:\n # The branches below must be a subset of the "
},
{
"path": ".github/workflows/cpp.yml",
"chars": 2982,
"preview": "name: C++ CI\n\non: [push]\n\njobs:\n\n short_fuzzing:\n runs-on: ubuntu-latest\n steps:\n - name: Build fuzzers\n "
},
{
"path": ".gitmodules",
"chars": 499,
"preview": "[submodule \"uSockets\"]\n\tpath = uSockets\n\turl = https://github.com/uNetworking/uSockets.git\n[submodule \"fuzzing/libEpollF"
},
{
"path": "GNUmakefile",
"chars": 412,
"preview": "# This is the GNU Make shim for Linux and macOS\n\nDESTDIR ?=\nprefix ?= /usr/local\n\nexamples: default\n\t./build examples\n\nc"
},
{
"path": "LICENSE",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "Makefile",
"chars": 640,
"preview": "# This is the NMake shim for Windows (WIP)\n# Example: $Env:WITH_ZLIB='0'; $ENV:WITH_LTO='0'; $Env:CC='clang'; $ENV:CFLAG"
},
{
"path": "README.md",
"chars": 6091,
"preview": "\n<div align=\"center\">\n<img src=\"https://raw.githubusercontent.com/uNetworking/uWebSockets/master/misc/logo.svg\" height=\""
},
{
"path": "autobahn/fuzzingclient.json",
"chars": 341,
"preview": "{\n \"outdir\": \"./reports/servers\",\n \"servers\": [\n {\n \"agent\": \"uWebSockets\",\n \"url\": \"ws://localhost:9001\""
},
{
"path": "autobahn/server-test.js",
"chars": 1210,
"preview": "// Based on https://github.com/denoland/fastwebsockets/blob/main/autobahn/server-test.js\n\nimport { $ } from \"https://den"
},
{
"path": "benchmarks/Makefile",
"chars": 703,
"preview": "default:\n\tg++ -flto -march=native parser.cpp -O3 -I../uSockets/src -o parser\n\tclang -flto -O3 -DLIBUS_USE_OPENSSL -I../u"
},
{
"path": "benchmarks/README.md",
"chars": 3828,
"preview": "# More recent benchmarks\nIt was a while ago this page was written (and it needs updating), so here are the most recent b"
},
{
"path": "benchmarks/broadcast_test.c",
"chars": 6413,
"preview": "/* This benchmark establishes _connections_ number of WebSocket\n clients, then iteratively performs the following:\n\n "
},
{
"path": "benchmarks/load_test.c",
"chars": 14312,
"preview": "#define _DEFAULT_SOURCE\n\n#ifdef __APPLE__\n#include <libkern/OSByteOrder.h>\n#define htobe16(x) OSSwapHostToBigInt16(x)\n#d"
},
{
"path": "benchmarks/parser.cpp",
"chars": 4291,
"preview": "/* This is a fuzz test of the websocket parser */\n\n#define WIN32_EXPORT\n\n/* We test the websocket parser */\n#include \".."
},
{
"path": "benchmarks/scale_test.c",
"chars": 8328,
"preview": "/* This is a scalability test for testing million(s) of pinging connections */\n\n#include <libusockets.h>\nint SSL;\n\n#incl"
},
{
"path": "build.c",
"chars": 4044,
"preview": "#include \"build.h\"\n\nint main(int argc, char **argv) {\n /* Some variables we need */\n char *CXXFLAGS = strncpy(call"
},
{
"path": "build.h",
"chars": 552,
"preview": "#define _CRT_SECURE_NO_WARNINGS\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include <stdarg.h>\n\nint env_"
},
{
"path": "cluster/README.md",
"chars": 14,
"preview": "# uWS Cluster\n"
},
{
"path": "examples/Broadcast.cpp",
"chars": 2742,
"preview": "/* We simply call the root header file \"App.h\", giving you uWS::App and uWS::SSLApp */\n#include \"App.h\"\n#include <time.h"
},
{
"path": "examples/BroadcastingEchoServer.cpp",
"chars": 2673,
"preview": "#include \"App.h\"\n\nstruct us_listen_socket_t *global_listen_socket;\n\nint main() {\n\n /* ws->getUserData returns one of "
},
{
"path": "examples/CachingApp.cpp",
"chars": 670,
"preview": "#include \"App.h\"\n#include <iostream>\n\nint main() {\n uWS::App app;\n\n /* Regular, non-cached response */\n app.get"
},
{
"path": "examples/Client.cpp",
"chars": 639,
"preview": "// This example is broken and doesn't do anything. It is a potential interface for a future potential client.\n// There i"
},
{
"path": "examples/Crc32.cpp",
"chars": 2204,
"preview": "#include \"App.h\"\n\n/* This is a good example for testing and showing the POST requests.\n * Anything you post (either with"
},
{
"path": "examples/EchoBody.cpp",
"chars": 1415,
"preview": "#include \"App.h\"\n\n/* Takes any method and echoes back the sent body. Can be used to test compliance of HTTP spec. */\n/* "
},
{
"path": "examples/EchoServer.cpp",
"chars": 2646,
"preview": "/* We simply call the root header file \"App.h\", giving you uWS::App and uWS::SSLApp */\n#include \"App.h\"\n\n/* This is a si"
},
{
"path": "examples/EchoServerThreaded.cpp",
"chars": 1939,
"preview": "#include \"App.h\"\n#include <thread>\n#include <algorithm>\n\nint main() {\n /* ws->getUserData returns one of these */\n "
},
{
"path": "examples/HelloWorld.cpp",
"chars": 611,
"preview": "#include \"App.h\"\n\n/* Note that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support */\n\nin"
},
{
"path": "examples/HelloWorldThreaded.cpp",
"chars": 954,
"preview": "#include \"App.h\"\n#include \"LocalCluster.h\"\n\nint main() {\n /* Note that SSL is disabled unless you build with WITH_OPE"
},
{
"path": "examples/Http3Server.cpp",
"chars": 3622,
"preview": "#ifdef LIBUS_USE_QUIC\n\n/* Do not rely on this API, it will change */\n#include \"Http3App.h\"\n#include <iostream>\n#include "
},
{
"path": "examples/HttpServer.cpp",
"chars": 2864,
"preview": "/* This is a simple HTTP(S) web server much like Python's SimpleHTTPServer */\n\n#include <App.h>\n\n/* Helpers for this exa"
},
{
"path": "examples/ParameterRoutes.cpp",
"chars": 826,
"preview": "#include \"App.h\"\n\n/* Note that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support */\n\nin"
},
{
"path": "examples/Precompress.cpp",
"chars": 2801,
"preview": "#include \"App.h\"\n\nint main() {\n /* ws->getUserData returns one of these */\n struct PerSocketData {\n /* Fill"
},
{
"path": "examples/README.md",
"chars": 216,
"preview": "# Examples\n\nMake sure to also check out the JavaScript examples, the TypeDoc documentation browser and the Discussions t"
},
{
"path": "examples/SecureGzipFileServer.cpp",
"chars": 6895,
"preview": "#include <iostream>\n#include <unordered_map>\n#include <mutex>\n#include <thread>\n#include <chrono>\n#include <filesystem>\n"
},
{
"path": "examples/ServerName.cpp",
"chars": 1041,
"preview": "#include \"App.h\"\n\n/* Note that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support */\n\nin"
},
{
"path": "examples/SmokeTest.cpp",
"chars": 1322,
"preview": "#include \"App.h\"\n\n/* This is not an example; it is a smoke test used in CI testing */\n\nstruct Stream {\n int offset;\n "
},
{
"path": "examples/UpgradeAsync.cpp",
"chars": 6011,
"preview": "/* We simply call the root header file \"App.h\", giving you uWS::App and uWS::SSLApp */\n#include \"App.h\"\n\n/* This is a si"
},
{
"path": "examples/UpgradeSync.cpp",
"chars": 3522,
"preview": "/* We simply call the root header file \"App.h\", giving you uWS::App and uWS::SSLApp */\n#include \"App.h\"\n\n/* This is a si"
},
{
"path": "examples/helpers/AsyncFileReader.h",
"chars": 3889,
"preview": "#include <map>\n#include <cstring>\n#include <fstream>\n#include <sstream>\n#include <iostream>\n#include <future>\n\n/* This i"
},
{
"path": "examples/helpers/AsyncFileStreamer.h",
"chars": 3259,
"preview": "#include <filesystem>\n\nstruct AsyncFileStreamer {\n\n std::map<std::string_view, AsyncFileReader *> asyncFileReaders;\n "
},
{
"path": "examples/helpers/Middleware.h",
"chars": 578,
"preview": "/* Middleware to fill out content-type */\ninline bool hasExt(std::string_view file, std::string_view ext) {\n if (ext."
},
{
"path": "examples/helpers/optparse.h",
"chars": 12227,
"preview": "/* Nicked from third-party https://github.com/skeeto/optparse 2018-09-24 */\n/* µWebSockets is not the origin of this sof"
},
{
"path": "fuzzing/AsyncEpollHelloWorld.cpp",
"chars": 1990,
"preview": "/* We rely on wrapped syscalls */\n#include \"libEpollFuzzer/epoll_fuzzer.h\"\n\n#include \"App.h\"\n\n/* We keep this one for te"
},
{
"path": "fuzzing/AsyncEpollHelloWorld.dict",
"chars": 232,
"preview": "\"get\"\n\"post\"\n\"get /\"\n\"http/1.1\"\n\"upgrade: websocket\"\n\"\\x0D\\x0A\"\n\"sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ==\"\n\"sec-webso"
},
{
"path": "fuzzing/EpollEchoServer.cpp",
"chars": 5402,
"preview": "/* We rely on wrapped syscalls */\n#include \"libEpollFuzzer/epoll_fuzzer.h\"\n\n#include \"App.h\"\n\n/* We keep this one for te"
},
{
"path": "fuzzing/EpollEchoServer.dict",
"chars": 232,
"preview": "\"get\"\n\"post\"\n\"get /\"\n\"http/1.1\"\n\"upgrade: websocket\"\n\"\\x0D\\x0A\"\n\"sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ==\"\n\"sec-webso"
},
{
"path": "fuzzing/EpollEchoServerPubSub.cpp",
"chars": 3505,
"preview": "/* We rely on wrapped syscalls */\n#include \"libEpollFuzzer/epoll_fuzzer.h\"\n\n#include \"App.h\"\n#include <vector>\n\n/* We ke"
},
{
"path": "fuzzing/EpollEchoServerPubSub.dict",
"chars": 232,
"preview": "\"get\"\n\"post\"\n\"get /\"\n\"http/1.1\"\n\"upgrade: websocket\"\n\"\\x0D\\x0A\"\n\"sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ==\"\n\"sec-webso"
},
{
"path": "fuzzing/EpollHelloWorld.cpp",
"chars": 6595,
"preview": "/* We rely on wrapped syscalls */\n#include \"libEpollFuzzer/epoll_fuzzer.h\"\n\n#include \"App.h\"\n\n/* We keep this one for te"
},
{
"path": "fuzzing/EpollHelloWorld.dict",
"chars": 232,
"preview": "\"get\"\n\"post\"\n\"get /\"\n\"http/1.1\"\n\"upgrade: websocket\"\n\"\\x0D\\x0A\"\n\"sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ==\"\n\"sec-webso"
},
{
"path": "fuzzing/Extensions.cpp",
"chars": 1762,
"preview": "/* This is a fuzz test of the websocket extensions parser */\n\n#define WIN32_EXPORT\n\n#include <cstdio>\n#include <string>\n"
},
{
"path": "fuzzing/Http.cpp",
"chars": 4295,
"preview": "/* This is a fuzz test of the http parser */\n\n#define WIN32_EXPORT\n\n#include \"helpers.h\"\n\n/* We test the websocket parse"
},
{
"path": "fuzzing/Makefile",
"chars": 3218,
"preview": "# You can select which sanitizer to use by setting this\nSANITIZER ?= address\n# These are set by OSS-Fuzz, we default to "
},
{
"path": "fuzzing/MultipartParser.cpp",
"chars": 1744,
"preview": "/* This is a fuzz test of the multipart parser */\n\n#define WIN32_EXPORT\n\n#include <cstdio>\n#include <string>\n#include <c"
},
{
"path": "fuzzing/PerMessageDeflate.cpp",
"chars": 2111,
"preview": "/* This is a fuzz test of the permessage-deflate module */\n\n#define WIN32_EXPORT\n\n#include <cstdio>\n#include <string>\n#i"
},
{
"path": "fuzzing/QueryParser.cpp",
"chars": 307,
"preview": "#include \"../src/QueryParser.h\"\n\n#include <string>\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t si"
},
{
"path": "fuzzing/QueryParser.dict",
"chars": 31,
"preview": "\"?\"\n\"%FF\"\n\"&\"\n\"+\"\n\"hello\"\n\"=\"\n\n"
},
{
"path": "fuzzing/README.md",
"chars": 1401,
"preview": "# Fuzz-testing of various parsers, mocked examples and system libraries\n\nA secure web server must be capable of receivin"
},
{
"path": "fuzzing/TopicTree.cpp",
"chars": 4593,
"preview": "#define WIN32_EXPORT\n\n#include \"helpers.h\"\n\n/* Test for the topic tree */\n#include \"../src/TopicTree.h\"\n\n#include <memor"
},
{
"path": "fuzzing/TopicTree.dict",
"chars": 46,
"preview": "\"S\"\n\"P\"\n\"A\"\n\"U\"\n\"+\"\n\"/\"\n\"#\"\n\"\\x00\\x00\\x00\\x00\""
},
{
"path": "fuzzing/WebSocket.cpp",
"chars": 1586,
"preview": "/* This is a fuzz test of the websocket parser */\n\n#define WIN32_EXPORT\n\n#include \"helpers.h\"\n\n/* We test the websocket "
},
{
"path": "fuzzing/helpers.h",
"chars": 1377,
"preview": "#ifndef HELPERS_H\n#define HELPERS_H\n\n/* Common helpers for fuzzing */\n\n#include <functional>\n#include <string_view>\n#inc"
},
{
"path": "libEpollBenchmarker/Makefile",
"chars": 430,
"preview": "# You need to link with wrapped syscalls\noverride CFLAGS += -Wl,--wrap=recv,--wrap=bind,--wrap=listen,--wrap=send,--wrap"
},
{
"path": "libEpollBenchmarker/epoll_benchmarker.cpp",
"chars": 2726,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <stdarg.h>\n\n#include <sys/timerf"
},
{
"path": "misc/READMORE.md",
"chars": 22953,
"preview": "# µWebSockets v19 user manual\n\nFor a list of frequently asked questions you may use the Discussions tab here on GitHub. "
},
{
"path": "misc/cert.pem",
"chars": 1229,
"preview": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAKLdQVPy90jjMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\nBAYTAkFVMRMwEQYDVQQIDApTb21"
},
{
"path": "misc/key.pem",
"chars": 1704,
"preview": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDuLsggR0gJOJJN\nXH6ZrrEfE61xt3rAZbeeTGUKQnV"
},
{
"path": "src/App.h",
"chars": 29656,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/AsyncSocket.h",
"chars": 14290,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/AsyncSocketData.h",
"chars": 2783,
"preview": "/*\n * Authored by Alex Hultman, 2018-2025.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/BloomFilter.h",
"chars": 2247,
"preview": "/*\n * Authored by Alex Hultman, 2018-2022.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/CachingApp.h",
"chars": 3304,
"preview": "#ifndef UWS_CACHINGAPP_H\n#define UWS_CACHINGAPP_H\n\n#include \"App.h\"\n#include <unordered_map>\n#include <string>\n#include "
},
{
"path": "src/ChunkedEncoding.h",
"chars": 7991,
"preview": "/*\n * Authored by Alex Hultman, 2018-2026.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/ClientApp.h",
"chars": 885,
"preview": "#include \"MoveOnlyFunction.h\"\n#include \"WebSocketContext.h\"\n#include <string>\n\nnamespace uWS {\n\n struct WebSocketClie"
},
{
"path": "src/Http3App.h",
"chars": 5315,
"preview": "#include \"App.h\"\n\n#include \"Http3Response.h\"\n#include \"Http3Request.h\"\n#include \"Http3Context.h\"\n\nnamespace uWS {\n\n s"
},
{
"path": "src/Http3Context.h",
"chars": 6404,
"preview": "\nextern \"C\" {\n#include \"quic.h\"\n}\n\n#include \"Http3ContextData.h\"\n#include \"Http3ResponseData.h\"\n\nnamespace uWS {\n str"
},
{
"path": "src/Http3ContextData.h",
"chars": 378,
"preview": "#include \"HttpRouter.h\"\n\nstruct Http3Response;\nstruct Http3Request;\n\nnamespace uWS {\n\n struct Http3ContextData {\n "
},
{
"path": "src/Http3Request.h",
"chars": 665,
"preview": "extern \"C\" {\n#include \"quic.h\"\n}\n\nnamespace uWS {\n\n struct Http3Request {\n\n std::string_view getHeader(std::st"
},
{
"path": "src/Http3Response.h",
"chars": 4419,
"preview": "extern \"C\" {\n#include \"quic.h\"\n}\n\n#include \"Http3ResponseData.h\"\n\nnamespace uWS {\n\n /* Is a quic stream */\n struct"
},
{
"path": "src/Http3ResponseData.h",
"chars": 600,
"preview": "#ifndef UWS_H3RESPONSEDATA_H\n#define UWS_H3RESPONSEDATA_H\n\n#include \"MoveOnlyFunction.h\"\n#include \"AsyncSocketData.h\"\n#i"
},
{
"path": "src/HttpContext.h",
"chars": 24143,
"preview": "/*\n * Authored by Alex Hultman, 2018-2026.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/HttpContextData.h",
"chars": 1781,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/HttpErrors.h",
"chars": 1961,
"preview": "/*\n * Authored by Alex Hultman, 2018-2023.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/HttpParser.h",
"chars": 32138,
"preview": "/*\n * Authored by Alex Hultman, 2018-2024.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/HttpResponse.h",
"chars": 25008,
"preview": "/*\n * Authored by Alex Hultman, 2018-2025.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/HttpResponseData.h",
"chars": 2952,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/HttpRouter.h",
"chars": 14125,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/LocalCluster.h",
"chars": 2121,
"preview": "/* This header is highly experimental and needs refactorings but will do for now */\n\n#include <thread>\n#include <algorit"
},
{
"path": "src/Loop.h",
"chars": 8066,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/LoopData.h",
"chars": 2995,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/MessageParser.h",
"chars": 2703,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/MoveOnlyFunction.h",
"chars": 14717,
"preview": "/*\nMIT License\n\nCopyright (c) 2020 Oleg Fatkhiev\n\nPermission is hereby granted, free of charge, to any person obtaining "
},
{
"path": "src/Multipart.h",
"chars": 8611,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/PerMessageDeflate.h",
"chars": 11295,
"preview": "/*\n * Authored by Alex Hultman, 2018-2021.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/ProxyParser.h",
"chars": 5040,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/QueryParser.h",
"chars": 4898,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/TopicTree.h",
"chars": 11550,
"preview": "/*\n * Authored by Alex Hultman, 2018-2021.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/Utilities.h",
"chars": 1436,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/WebSocket.h",
"chars": 19167,
"preview": "/*\n * Authored by Alex Hultman, 2018-2021.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/WebSocketContext.h",
"chars": 22698,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/WebSocketContextData.h",
"chars": 3724,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/WebSocketData.h",
"chars": 2781,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/WebSocketExtensions.h",
"chars": 9148,
"preview": "/*\n * Authored by Alex Hultman, 2018-2021.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/WebSocketHandshake.h",
"chars": 5576,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "src/WebSocketProtocol.h",
"chars": 18715,
"preview": "/*\n * Authored by Alex Hultman, 2018-2020.\n * Intellectual property of third-party.\n\n * Licensed under the Apache Licens"
},
{
"path": "tests/BloomFilter.cpp",
"chars": 2775,
"preview": "#include \"../src/BloomFilter.h\"\n\n#include <cassert>\n#include <vector>\n#include <string>\n#include <algorithm>\n#include <i"
},
{
"path": "tests/ChunkedEncoding.cpp",
"chars": 7225,
"preview": "#include <iostream>\n#include <cassert>\n#include <sstream>\n#include <iomanip>\n#include <vector>\n#include <climits>\n\n#incl"
},
{
"path": "tests/ExtensionsNegotiator.cpp",
"chars": 4287,
"preview": "/* This is a temporary fix since we do not support this mode with pub/sub yet */\n#define UWS_ALLOW_SHARED_AND_DEDICATED_"
},
{
"path": "tests/HttpParser.cpp",
"chars": 1510,
"preview": "#include <iostream>\n#include <cassert>\n\n#include \"../src/HttpParser.h\"\n\nint main() {\n /* Parser needs at least 8 byte"
},
{
"path": "tests/HttpRouter.cpp",
"chars": 12038,
"preview": "#include \"../src/HttpRouter.h\"\n\n#include <cassert>\n#include <iostream>\n\nvoid testMethodPriority() {\n std::cout << \"Te"
},
{
"path": "tests/Makefile",
"chars": 970,
"preview": "default:\n\t$(CXX) -std=c++17 -fsanitize=address Query.cpp -o Query\n\t./Query\n\t$(CXX) -std=c++17 -fsanitize=address Chunked"
},
{
"path": "tests/Query.cpp",
"chars": 1268,
"preview": "#include <iostream>\n#include <cassert>\n\n#include \"../src/QueryParser.h\"\n\nint main() {\n\n {\n std::string buf = \""
},
{
"path": "tests/README.md",
"chars": 1260,
"preview": "# Testing\nThis folder holds some unit tests and a smoke test. These are run by GitHub Actions on every push. There are m"
},
{
"path": "tests/TopicTree.cpp",
"chars": 6190,
"preview": "#include \"../src/TopicTree.h\"\n\n#include <cassert>\n#include <iostream>\n\n/* Modifying the topicTree inside callback is not"
},
{
"path": "tests/smoke.mjs",
"chars": 2809,
"preview": "/* This smoke test runs against the Crc32 example program for now, but this example will be extended for more tests */\n\n"
}
]
About this extraction
This page contains the full source code of the uNetworking/uWebSockets GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 115 files (567.0 KB), approximately 141.6k tokens, and a symbol index with 346 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.