Remove unused files in bun-uws and bun-usockets (#19435)

This commit is contained in:
Ashcon Partovi
2025-05-02 10:50:39 -07:00
committed by GitHub
parent d8a69d6823
commit e2ed4f33a9
29177 changed files with 0 additions and 173959 deletions

View File

@@ -136,9 +136,6 @@
"**/*.xcscheme": true,
"**/*.xcodeproj": true,
"**/*.i": true,
// uws WebSocket.cpp conflicts with webcore WebSocket.cpp
"packages/bun-uws/fuzzing": true,
},
"files.associations": {
"*.css": "tailwindcss",

View File

@@ -1 +0,0 @@
*.h linguist-language=C

View File

@@ -1,5 +0,0 @@
*.i
*.o
*.s
*.bc
*.ii

View File

@@ -1,57 +0,0 @@
extern "C" {
void *sni_new();
void sni_free(void *sni, void (*cb)(void *user));
int sni_add(void *sni, const char *hostname, void *user);
void *sni_remove(void *sni, const char *hostname);
void *sni_find(void *sni, const char *hostname);
}
#include <assert.h>
#include <stdio.h>
/* Todo: replace 13, 14 and 15 with malloc */
//void *WILDCARD_GOOGLE_COM = strdup("*.google.com");
//void *TEST_GOOGLE_COM = strdup("test.google.com");
void sni_free_cb(void *user) {
printf("Freeing %p\n", user);
}
int main() {
void *sni = sni_new();
/* Adding should succeed */
assert(sni_add(sni, "*.google.com", 13) == 0);
assert(sni_add(sni, "test.google.com", 14) == 0);
/* Adding the same name should not overwrite existing */
assert(sni_add(sni, "*.google.com", 15) != 0);
assert(sni_find(sni, "anything.google.com") == 13);
assert(sni_find(sni, "docs.google.com") == 13);
assert(sni_find(sni, "*.google.com") == 13);
assert(sni_find(sni, "test.google.com") == 14);
assert(sni_find(sni, "yolo.nothing.com") == 0);
assert(sni_find(sni, "yolo.google.com") == 13);
/* Removing should work */
assert(sni_remove(sni, "test.google.com") == 14);
assert(sni_find(sni, "test.google.com") == 13);
assert(sni_remove(sni, "*.google.com") == 13);
assert(sni_find(sni, "test.google.com") == 0);
/* Removing parent with data should not remove child with data */
assert(sni_add(sni, "www.google.com", 16) == 0);
assert(sni_add(sni, "www.google.com.au.ck.uk", 17) == 0);
assert(sni_find(sni, "www.google.com") == 16);
assert(sni_find(sni, "www.google.com.au.ck.uk") == 17);
assert(sni_remove(sni, "www.google.com.yolo") == 0);
assert(sni_remove(sni, "www.google.com.au.ck.uk") == 17);
assert(sni_find(sni, "www.google.com") == 16);
/* Free should not leave anything remaining (test with ASAN leaksanitizer) */
sni_free(sni, sni_free_cb);
printf("OK\n");
}

View File

@@ -1,67 +0,0 @@
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@v4
# 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}}"

View File

@@ -1,90 +0,0 @@
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
if-no-files-found: "error"
build_windows:
runs-on: windows
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 unit tests
run: $Env:CC='clang'; $Env:CXX='clang++'; make -C uWebSockets/tests
- 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: 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
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

View File

@@ -1,81 +0,0 @@
{
"files.associations": {
"*.lock": "yarnlock",
"socket.h": "c",
"in.h": "c",
"string.h": "c",
"tcp.h": "c",
"vector": "cpp",
"string_view": "cpp",
"array": "cpp",
"future": "cpp",
"istream": "cpp",
"locale": "cpp",
"memory": "cpp",
"thread": "cpp",
"tuple": "cpp",
"variant": "cpp",
"__bit_reference": "cpp",
"__bits": "cpp",
"__config": "cpp",
"__debug": "cpp",
"__errc": "cpp",
"__hash_table": "cpp",
"__locale": "cpp",
"__mutex_base": "cpp",
"__node_handle": "cpp",
"__split_buffer": "cpp",
"__threading_support": "cpp",
"__tree": "cpp",
"__tuple": "cpp",
"any": "cpp",
"atomic": "cpp",
"bit": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"charconv": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"complex": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"exception": "cpp",
"forward_list": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"limits": "cpp",
"list": "cpp",
"map": "cpp",
"mutex": "cpp",
"new": "cpp",
"optional": "cpp",
"ostream": "cpp",
"ratio": "cpp",
"set": "cpp",
"sstream": "cpp",
"stack": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"string": "cpp",
"system_error": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"__verbose_abort": "cpp",
"algorithm": "cpp"
}
}

View File

@@ -1,24 +0,0 @@
# 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) build.c -o build

View File

@@ -1,7 +0,0 @@
default:
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 -o load_test
clang++ -flto -O3 -DLIBUS_USE_OPENSSL `ls *.o | grep -Ev "broadcast_test|load_test"` -lssl -lcrypto -o scale_test

View File

@@ -1,36 +0,0 @@
# 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 - if anything would ever be faster, it would only be so by less than 2%. We know of no such project.
Http | WebSockets
--- | ---
![](../misc/bigshot_lineup.png) | ![](../misc/websocket_lineup.png)
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.

View File

@@ -1,196 +0,0 @@
/* 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);
}

View File

@@ -1,307 +0,0 @@
/* This is a simple yet efficient WebSocket server benchmark much like WRK */
#define _BSD_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>
int SSL;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Whatever type we selected (compressed or not) */
unsigned char *web_socket_request;
int web_socket_request_size;
char *upgrade_request;
int upgrade_request_length;
/* Compressed message */
unsigned char web_socket_request_deflate[13] = {
130 | 64, 128 | 7,
0, 0, 0, 0,
0xf2, 0x48, 0xcd, 0xc9, 0xc9, 0x07, 0x00
};
/* Not compressed */
unsigned char web_socket_request_text_small[26] = {130, 128 | 20, 1, 2, 3, 4};
unsigned int web_socket_request_text_size = 26;
unsigned char *web_socket_request_text = web_socket_request_text_small;
/* Called to swap from small text message to big text message */
void init_big_message(unsigned int size) {
if (size < 65536) {
printf("Error: message size must be bigger\n");
exit(0);
}
web_socket_request_text_size = size + 6 + 8;
web_socket_request_text = malloc(web_socket_request_text_size);
web_socket_request_text[0] = 130;
web_socket_request_text[1] = 255;
uint64_t msg_size = htobe64(size);
memcpy(&web_socket_request_text[2], &msg_size, 8);
web_socket_request_text[10] = 1;
web_socket_request_text[10] = 2;
web_socket_request_text[10] = 3;
web_socket_request_text[10] = 4;
}
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 = malloc(web_socket_request_text_size);
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;
}
char request_deflate[] = "GET / HTTP/1.1\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"
"Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n"
"Host: server.example.com\r\n"
"Sec-WebSocket-Version: 13\r\n\r\n";
char request_text[] = "GET / HTTP/1.1\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"
//"Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n"
"Host: server.example.com\r\n"
"Sec-WebSocket-Version: 13\r\n\r\n";
char *host;
int port;
int connections;
int responses;
struct http_socket {
/* How far we have streamed our websocket request */
int offset;
/* How far we have streamed our upgrade request */
int upgrade_offset;
/* Whether or not we have received the upgrade response */
int is_upgraded;
/* How many bytes we expect to be echoed back to us before we consider the echo done */
int outstanding_bytes;
};
/* We don't need any of these */
void on_wakeup(struct us_loop_t *loop) {
}
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) {
us_socket_context_connect(SSL, us_socket_context(SSL, s), host, port, NULL, 0, sizeof(struct http_socket));
} else {
printf("Running benchmark now...\n");
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 < upgrade_request_length) {
http_socket->upgrade_offset += us_socket_write(SSL, s, upgrade_request + http_socket->upgrade_offset, upgrade_request_length - http_socket->upgrade_offset, 0);
/* Now we should be */
if (http_socket->upgrade_offset == upgrade_request_length) {
next_connection(s);
}
} else {
/* Stream whatever is remaining of the request */
http_socket->offset += us_socket_write(SSL, s, (char *) web_socket_request + http_socket->offset, web_socket_request_size - http_socket->offset, 0);
}
return s;
}
struct us_socket_t *on_http_socket_close(struct us_socket_t *s, int code, void *reason) {
printf("Closed!\n");
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);
if (http_socket->is_upgraded) {
/* If we are upgraded we now count to see if we receive the corect echo */
http_socket->outstanding_bytes -= length;
if (http_socket->outstanding_bytes == 0) {
/* We got exactly the correct amount of bytes back, send another message */
http_socket->offset = us_socket_write(SSL, s, (char *) web_socket_request, web_socket_request_size, 0);
http_socket->outstanding_bytes = web_socket_request_size - 4;
/* Increase stats */
responses++;
} else if (http_socket->outstanding_bytes < 0) {
/* This should never happen */
printf("ERROR: outstanding bytes negative!");
exit(0);
}
} else {
/* We assume the last 4 bytes will be delivered in one chunk */
if (length >= 4 && memcmp(data + length - 4, "\r\n\r\n", 4) == 0) {
/* We are upgraded so start sending the message for echoing */
http_socket->offset = us_socket_write(SSL, s, (char *) web_socket_request, web_socket_request_size, 0);
/* Server will echo back the same message minus 4 bytes for mask */
http_socket->outstanding_bytes = web_socket_request_size - 4;
http_socket->is_upgraded = 1;
}
}
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;
/* Send an upgrade request */
http_socket->upgrade_offset = us_socket_write(SSL, s, upgrade_request, upgrade_request_length, 0);
if (http_socket->upgrade_offset == upgrade_request_length) {
next_connection(s);
}
return s;
}
struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) {
/* Print current statistics */
printf("Msg/sec: %f\n", ((float)responses) / LIBUS_TIMEOUT_GRANULARITY);
responses = 0;
us_socket_timeout(SSL, s, LIBUS_TIMEOUT_GRANULARITY);
return s;
}
int main(int argc, char **argv) {
/* Parse host and port */
if (argc != 6 && argc != 7) {
printf("Usage: connections host port ssl deflate [size_kb]\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]);
if (atoi(argv[5])) {
/* Set up deflate */
web_socket_request = web_socket_request_deflate;
web_socket_request_size = sizeof(web_socket_request_deflate);
upgrade_request = request_deflate;
upgrade_request_length = sizeof(request_deflate) - 1;
} else {
/* Only if we are NOT using defalte can we support testing with 100mb for now */
if (argc == 7) {
int size_kb = atoi(argv[6]);
printf("Using message size of %d kB\n", size_kb);
/* Size has to be in KB since the minimal size for medium is 1kb */
if (size_kb <= 64) {
init_medium_message(size_kb * 1024);
} else {
init_big_message(size_kb * 1024);
}
}
web_socket_request = web_socket_request_text;
web_socket_request_size = web_socket_request_text_size;
upgrade_request = request_text;
upgrade_request_length = sizeof(request_text) - 1;
}
/* 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);
/* Start making HTTP connections */
us_socket_context_connect(SSL, http_context, host, port, NULL, 0, sizeof(struct http_socket));
us_loop_run(loop);
}

View File

@@ -1,276 +0,0 @@
/* 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);
}

View File

@@ -1,91 +0,0 @@
#include "build.h"
int main(int argc, char **argv) {
/* Some variables we need */
char *CXXFLAGS = strcpy(calloc(1024, 1), maybe(getenv("CXXFLAGS")));
char *CFLAGS = strcpy(calloc(1024, 1), maybe(getenv("CFLAGS")));
char *LDFLAGS = strcpy(calloc(1024, 1), maybe(getenv("LDFLAGS")));
char *CC = strcpy(calloc(1024, 1), or_else(getenv("CC"), "cc"));
char *CXX = strcpy(calloc(1024, 1), or_else(getenv("CXX"), "g++"));
char *EXEC_SUFFIX = strcpy(calloc(1024, 1), maybe(getenv("EXEC_SUFFIX")));
char *EXAMPLE_FILES[] = {"Http3Server", "Broadcast", "HelloWorld", "Crc32", "ServerName",
"EchoServer", "BroadcastingEchoServer", "UpgradeSync", "UpgradeAsync"};
strcat(CXXFLAGS, " -O3 -Wpedantic -Wall -Wextra -Wsign-conversion -Wconversion -std=c++20 -Isrc -IuSockets/src");
strcat(LDFLAGS, " uSockets/*.o");
// By default we use LTO, but Windows does not support it
if (!env_is("WITH_LTO", "0")) {
strcat(CXXFLAGS, " -flto");
}
// 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")) {
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)) {
return -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");
}
}

View File

@@ -1,28 +0,0 @@
#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);
}

View File

@@ -1 +0,0 @@
# uWS Cluster

View File

@@ -1,80 +0,0 @@
/* 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();
}

View File

@@ -1,76 +0,0 @@
#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();
}

View File

@@ -1,22 +0,0 @@
// This example is broken and doesn't do anything. It is a potential interface for a future potential client.
#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();
}

View File

@@ -1,71 +0,0 @@
#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;
}

View File

@@ -1,56 +0,0 @@
/* 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_4KB | 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) {
ws->send(message, opCode, false);
},
.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();
}

View File

@@ -1,58 +0,0 @@
#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();
});
}

View File

@@ -1,20 +0,0 @@
#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;
}

View File

@@ -1,40 +0,0 @@
#include "App.h"
#include <thread>
#include <algorithm>
#include <mutex>
/* Note that SSL is disabled unless you build with WITH_OPENSSL=1 */
const int SSL = 1;
std::mutex stdoutMutex;
int main() {
/* Overly simple hello world app, 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([]() {
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) {
stdoutMutex.lock();
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(SSL, (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;
}
stdoutMutex.unlock();
}).run();
});
});
std::for_each(threads.begin(), threads.end(), [](std::thread *t) {
t->join();
});
}

View File

@@ -1,100 +0,0 @@
#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

View File

@@ -1,90 +0,0 @@
/* 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;
struct us_socket_context_options_t 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 */
struct us_socket_context_options_t 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;
}

View File

@@ -1,3 +0,0 @@
# 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.

View File

@@ -1,27 +0,0 @@
#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();
}

View File

@@ -1,124 +0,0 @@
/* 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->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, 0);
}, 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();
}

View File

@@ -1,77 +0,0 @@
/* 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();
}

View File

@@ -1,130 +0,0 @@
#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;
}
};

View File

@@ -1,84 +0,0 @@
#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());
if (!chunk.length() || res->tryEnd(chunk, asyncFileReader->getFileSize()).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() < asyncFileReader->getFileSize()) {
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;
});
}
}
};

View File

@@ -1,19 +0,0 @@
/* 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;
}

View File

@@ -1,407 +0,0 @@
/* 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 */

View File

@@ -1,59 +0,0 @@
/* 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;
}
}

View File

@@ -1,12 +0,0 @@
"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: "
" "

View File

@@ -1,156 +0,0 @@
/* 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;
}
}

View File

@@ -1,12 +0,0 @@
"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: "
" "

View File

@@ -1,98 +0,0 @@
/* 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;
}
}

View File

@@ -1,12 +0,0 @@
"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: "
" "

View File

@@ -1,178 +0,0 @@
/* 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;
}
}

View File

@@ -1,12 +0,0 @@
"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: "
" "

View File

@@ -1,51 +0,0 @@
/* 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;
}

View File

@@ -1,155 +0,0 @@
/* 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 */
void *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;
}, [](void *user) -> void * {
/* Return break */
return nullptr;
});
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;
}

View File

@@ -1,47 +0,0 @@
# 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/*

View File

@@ -1,62 +0,0 @@
/* 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;
}

View File

@@ -1,64 +0,0 @@
/* 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;
}

View File

@@ -1,13 +0,0 @@
#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;
}

View File

@@ -1,7 +0,0 @@
"?"
"%FF"
"&"
"+"
"hello"
"="

View File

@@ -1,27 +0,0 @@
# 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.

View File

@@ -1,128 +0,0 @@
#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;
}

View File

@@ -1,8 +0,0 @@
"S"
"P"
"A"
"U"
"+"
"/"
"#"
"\x00\x00\x00\x00"

View File

@@ -1,59 +0,0 @@
/* 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;
}

View File

@@ -1,51 +0,0 @@
#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

View File

@@ -1,8 +0,0 @@
# You need to link with wrapped syscalls
override CFLAGS += -Wl,--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
# Include uSockets and uWebSockets
override CFLAGS += -DUWS_NO_ZLIB -I./uWebSockets/src -I./uSockets/src
default:
clang++ -std=c++17 -fsanitize=address,fuzzer test.c $(CFLAGS) -o test uSockets/uSockets.a

View File

@@ -1,38 +0,0 @@
# libEpollFuzzer - fuzzing for Linux servers
This mock implementation of the [epoll/socket](https://en.wikipedia.org/wiki/Epoll) syscalls allows you to test intricate edge cases and find bugs in mission critical software - all within minutes. It builds on LLVM's [libFuzzer](http://llvm.org/docs/LibFuzzer.html) and operates based on nothing but fuzz data, being entirely deterministic and reproducible.
Where [syzkaller](https://github.com/google/syzkaller) is a *user space process* fuzzing the **kernel**, libEpollFuzzer is mocking the *kernel* and fuzzing the **user space process**.
## Can you find the bug?
The following code runs fine in most cases but has a critical security bug that can be hard to trigger - can you find it?
```c++
int epfd = epoll_create1();
int lsfd = listen(asdasdasd);
int ready_fd = epoll_wait(epfd, lalalala);
for (all ready fds)
int length = recv(buf, 24234234);
//copy from 0 and length
```
## Let's find it!
gif here of finding the bug
## A more complex case
Fuzzing the entire uSockets library takes no more than a few linker flags. Other libraries that may benefit from fuzzing include libuv, libev, libevent, ASIO and the like.
## How it works
<img src="epollFuzzer.svg" height="200" />
Any user space process will communicate with the kernel via syscalls. While possible for the kernel to trigger user space signals, these are typically not used to communicate fine grained events. Instead the user space process will "pull" events in batches from the kernel in "event-loop iterations". This by calling the potentially blocking syscall epoll_wait. When the kernel resumes user space execution, the process will continue to process the events and run callbacks/co-routines as it wishes. Data is then "pushed" to kernel space by calls to send, write, etc. This completes the primary input/output cycle.
Linking to libEpollFuzzer, certain syscalls are wrapped at the linker stage. These calls, as made by the server, then run mock variants where fuzz data from libFuzzer is used to control the outcome / result. Because the fuzz data is randomly evolving based on coverage, so does the execution order / behavior of your entire async server. Hence, it is possible to easily uncover edge cases such as the one presented above - cases which might be hard to trigger in real-world use cases or testing.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 58 KiB

View File

@@ -1,751 +0,0 @@
/* Welcome to libEpollFuzzer - a mock implementation of the epoll/socket syscalls */
/* Current implementation is extremely experimental and trashy, mind you */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
//#include <threads.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
// todo: add connect, donät pass invalid-FD to real syscalls
// getaddrinfo should return inet6 somtimes and sometimes wrong family (done)
// accept4 should produce inet6 sometimes (done)
// socket syscall should fail with given invalid family (done)
// listen syscall should fail sometimes (done)
/* Currently read, close, fcntl are wrapped to real syscalls */
/* TODO: Our FDs should start at 1024 while actual real FDs should be reserved from 0 to 1023 and passed to actual
* real syscalls so that we can co-exist with overlapping syscalls like read, open, write, close */
//#define PRINTF_DEBUG
/* The test case */
void test();
void teardown();
#ifdef __cplusplus
extern "C" {
#endif
struct file {
/* Every file has a type; socket, event, timer, epoll */
int type;
/* We assume there can only be one event-loop at any given point in time,
* so every file holds its own epoll_event */
struct epoll_event epev;
/* A file may be added to an epfd by linking it in a list */
struct file *prev, *next;
};
/* If FD is less than this, it should be passed to REAL syscall.
* We never produce FDs lower than this (except for -1 on error) */
const int RESERVED_SYSTEM_FDS = 1024;
/* Map from some collection of integers to a shared extensible struct of data */
const int MAX_FDS = 1000;
struct file *fd_to_file[MAX_FDS];
const int FD_TYPE_EPOLL = 0;
const int FD_TYPE_TIMER = 1;
const int FD_TYPE_EVENT = 2;
const int FD_TYPE_SOCKET = 3;
int num_fds = 0;
/* Keeping track of cunsumable data */
unsigned char *consumable_data;
int consumable_data_length;
void set_consumable_data(const unsigned char *new_data, int new_length) {
consumable_data = (unsigned char *) new_data;
consumable_data_length = new_length;
}
/* Returns non-null on error */
int consume_byte(unsigned char *b) {
if (consumable_data_length) {
*b = consumable_data[0];
consumable_data++;
consumable_data_length--;
return 0;
}
return -1;
}
/* Keeping track of FDs */
/* Returns -1 on error, or RESERVED_SYSTEM_FDS and above */
int allocate_fd() {
// this can be massively optimized by having a list of free blocks or the like
for (int fd = 0; fd < MAX_FDS; fd++) {
if (!fd_to_file[fd]) {
num_fds++;
return fd + RESERVED_SYSTEM_FDS;
}
}
return -1;
}
/* This one should set the actual file for this FD */
void init_fd(int fd, int type, struct file *f) {
if (fd >= RESERVED_SYSTEM_FDS) {
fd_to_file[fd - RESERVED_SYSTEM_FDS] = f;
fd_to_file[fd - RESERVED_SYSTEM_FDS]->type = type;
fd_to_file[fd - RESERVED_SYSTEM_FDS]->next = NULL;
fd_to_file[fd - RESERVED_SYSTEM_FDS]->prev = NULL;
}
}
struct file *map_fd(int fd) {
if (fd >= RESERVED_SYSTEM_FDS && fd < MAX_FDS + RESERVED_SYSTEM_FDS) {
return fd_to_file[fd - RESERVED_SYSTEM_FDS];
}
return NULL;
}
/* This one should remove the FD from any pollset by calling epoll_ctl remove */
int free_fd(int fd) {
if (fd >= RESERVED_SYSTEM_FDS && fd < MAX_FDS + RESERVED_SYSTEM_FDS) {
if (fd_to_file[fd - RESERVED_SYSTEM_FDS]) {
fd_to_file[fd - RESERVED_SYSTEM_FDS] = 0;
num_fds--;
return 0;
}
}
return -1;
}
/* The epoll syscalls */
struct epoll_file {
struct file base;
/* A doubly linked list for polls awaiting events */
struct file *poll_set_head, *poll_set_tail;
};
/* This function is O(n) and does not consume any fuzz data, but will fail if run out of FDs */
int __wrap_epoll_create1(int flags) {
/* Todo: check that we do not allocate more than one epoll FD */
int fd = allocate_fd();
if (fd != -1) {
struct epoll_file *ef = (struct epoll_file *)malloc(sizeof(struct epoll_file));
/* Init the epoll_file */
ef->poll_set_head = NULL;
ef->poll_set_tail = NULL;
init_fd(fd, FD_TYPE_EPOLL, (struct file *)ef);
}
#ifdef PRINTF_DEBUG
printf("epoll_create1 returning epfd: %d\n", fd);
#endif
return fd;
}
// this function cannot be called inside an iteration! it changes the list
/* This function is O(1) and does not consume any fuzz data */
int __wrap_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) {
struct epoll_file *ef = (struct epoll_file *)map_fd(epfd);
if (!ef) {
return -1;
}
struct file *f = (struct file *)map_fd(fd);
if (!f) {
return -1;
}
/* We add new polls in the head */
if (op == EPOLL_CTL_ADD) {
// if there is a head already
if (ef->poll_set_head) {
ef->poll_set_head->prev = f;
// then it will be our next
f->next = ef->poll_set_head;
} else {
// if there was no head then we became the tail also
ef->poll_set_tail = f;
}
// we are now the head in any case
ef->poll_set_head = f;
f->epev = *event;
} else if (op == EPOLL_CTL_MOD) {
/* Modifying is simply changing the file itself */
f->epev = *event;
} else if (op == EPOLL_CTL_DEL) {
if (f->prev) {
f->prev->next = f->next;
} else {
ef->poll_set_head = f->next;
}
if (f->next) {
f->next->prev = f->prev;
} else {
// tail ska vara vår.prev
ef->poll_set_tail = f->prev;
}
// a file that is not in the list should be reset to NULL
f->prev = NULL;
f->next = NULL;
}
/* You have to poll for errors and hangups */
f->epev.events |= EPOLLERR | EPOLLHUP;
return 0;
}
/* This function is O(n) and consumes fuzz data and might trigger teardown callback */
int __wrap_epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout) {
//printf("epoll_wait: %d\n", 0);
#ifdef PRINTF_DEBUG
printf("Calling epoll_wait\n");
#endif
struct epoll_file *ef = (struct epoll_file *)map_fd(epfd);
if (!ef) {
return -1;
}
if (consumable_data_length) {
int ready_events = 0;
for (struct file *f = ef->poll_set_head; f; f = f->next) {
/* Consume one fuzz byte, AND it with the event */
if (!consumable_data_length) {
// break if we have no data
break;
}
// here we have the main condition that drives everything
int ready_event = consumable_data[0] & f->epev.events;
// consume the byte
consumable_data_length--;
consumable_data++;
if (ready_event) {
if (ready_events < maxevents) {
events[ready_events] = f->epev;
// todo: the event should be masked by the byte, not everything it wants shold be given all the time!
events[ready_events++].events = ready_event;
} else {
// we are full, break
break;
}
}
}
return ready_events;
} else {
#ifdef PRINTF_DEBUG
printf("Calling teardown\n");
#endif
teardown();
// after shutting down the listen socket we clear the whole list (the bug in epoll_ctl remove)
// so the below loop doesn't work - we never close anything more than the listen socket!
/* You don't really need to emit teardown, you could simply emit error on every poll */
int ready_events = 0;
#ifdef PRINTF_DEBUG
printf("Emitting error on every remaining FD\n");
#endif
for (struct file *f = ef->poll_set_head; f; f = f->next) {
if (f->type == FD_TYPE_SOCKET) {
if (ready_events < maxevents) {
events[ready_events] = f->epev;
// todo: the event should be masked by the byte, not everything it wants shold be given all the time!
events[ready_events++].events = EPOLLERR | EPOLLHUP;
} else {
// we are full, break
break;
}
}
}
#ifdef PRINTF_DEBUG
printf("Ready events: %d\n", ready_events);
#endif
return ready_events;
}
}
/* The socket syscalls */
struct socket_file {
struct file base;
/* We store socket addresses created in accept4 */
union {
struct sockaddr_in6 in6;
struct sockaddr_in in;
} addr;
/* The size of sockaddr_in6 or sockaddr_in as a whole */
socklen_t len;
};
extern int __real_read(int fd, void *buf, size_t count);
int __wrap_read(int fd, void *buf, size_t count) {
if (fd < RESERVED_SYSTEM_FDS) {
return __real_read(fd, buf, count);
}
#ifdef PRINTF_DEBUG
printf("Wrapped read\n");
#endif
/* Let's try and clear the buffer first */
//memset(buf, 0, count);
struct file *f = map_fd(fd);
if (!f) {
return -1;
}
errno = 0;
if (f->type == FD_TYPE_SOCKET) {
if (!consumable_data_length) {
errno = EWOULDBLOCK;
return -1;
} else {
int data_available = (unsigned char) consumable_data[0];
consumable_data_length--;
consumable_data++;
if (consumable_data_length < data_available) {
data_available = consumable_data_length;
}
if (count < data_available) {
data_available = count;
}
memcpy(buf, consumable_data, data_available);
consumable_data_length -= data_available;
consumable_data += data_available;
return data_available;
}
}
if (f->type == FD_TYPE_EVENT) {
memset(buf, 1, 8);
return 8;
}
if (f->type == FD_TYPE_TIMER) {
memset(buf, 1, 8);
return 8;
}
return -1;
}
/* We just ignore the extra flag here */
int __wrap_recv(int sockfd, void *buf, size_t len, int flags) {
return __wrap_read(sockfd, buf, len);
}
int __wrap_send(int sockfd, const void *buf, size_t len, int flags) {
if (consumable_data_length) {
/* We can send len scaled by the 1 byte */
unsigned char scale = consumable_data[0];
consumable_data++;
consumable_data_length--;
int written = float(scale) / 255.0f * len;
if (written == 0) {
errno = EWOULDBLOCK;
} else {
errno = 0;
}
return written;
} else {
return -1;
}
}
int __wrap_sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen) {
return __wrap_send(sockfd, buf, len, flags);
}
int __wrap_bind() {
return 0;
}
int __wrap_setsockopt() {
return 0;
}
extern int __real_fcntl(int fd, int cmd, ... /* arg */ );
int __wrap_fcntl(int fd, int cmd, ... /* arg */) {
if (fd < RESERVED_SYSTEM_FDS) {
va_list args;
va_start(args, cmd);
int ret = __real_fcntl(fd, cmd, args);
va_end(args);
return ret;
}
return 0;
}
/* Addrinfo */
int __wrap_getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res) {
//printf("Wrapped getaddrinfo\n");
struct addrinfo default_hints = {};
if (!hints) {
hints = &default_hints;
}
unsigned char b;
if (consume_byte(&b)) {
return -1;
}
/* This one should be thread_local */
static /*thread_local*/ struct addrinfo ai;
ai.ai_flags = hints->ai_flags;
ai.ai_socktype = hints->ai_socktype;
ai.ai_protocol = hints->ai_protocol;
if (b > 127) {
ai.ai_family = AF_INET;//hints->ai_family;
} else {
ai.ai_family = AF_INET6;//hints->ai_family;
}
/* This one is for generating the wrong family (maybe invalid?) */
if (b == 0) {
ai.ai_family = hints->ai_family;
}
ai.ai_next = NULL;
ai.ai_canonname = NULL; // fel
// these should depend on inet6 or inet */
ai.ai_addrlen = 4; // fel
ai.ai_addr = NULL; // ska peka på en sockaddr!
// we need to return an addrinfo with family AF_INET6
*res = &ai;
return 0;
}
int __wrap_freeaddrinfo() {
return 0;
}
/* This one should return the same address as accept4 did produce */
int __wrap_getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
struct file *f = map_fd(sockfd);
if (!f) {
return -1;
}
// todo: this could fail with -1 also (consume a byte)?
if (f->type == FD_TYPE_SOCKET) {
struct socket_file *sf = (struct socket_file *) f;
if (addr) {
memcpy(addr, &sf->addr, sf->len);
*addrlen = sf->len;
}
return 0;
}
return -1;
}
int __wrap_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
/* We must end with -1 since we are called in a loop */
unsigned char b;
if (consume_byte(&b)) {
return -1;
}
/* This rule might change, anything below 10 is accepted */
if (b < 10) {
int fd = allocate_fd();
if (fd != -1) {
/* Allocate the file */
struct socket_file *sf = (struct socket_file *) malloc(sizeof(struct socket_file));
/* Init the file */
/* Here we need to create a socket FD and return */
init_fd(fd, FD_TYPE_SOCKET, (struct file *)sf);
/* We need to provide an addr */
/* Begin by setting it to an empty in6 address */
memset(&sf->addr, 0, sizeof(struct sockaddr_in6));
sf->len = sizeof(struct sockaddr_in6);
sf->addr.in6.sin6_family = AF_INET6;
/* Opt-in to ipv4 */
if (b < 5) {
memset(&sf->addr, 0, sizeof(struct sockaddr_in6));
sf->len = sizeof(struct sockaddr_in);
sf->addr.in.sin_family = AF_INET;
}
if (addr) {
/* Copy from socket to addr */
memcpy(addr, &sf->addr, sf->len);
}
}
return fd;
}
return -1;
}
int __wrap_listen() {
/* Listen consumes one byte and fails on -1 */
unsigned char b;
if (consume_byte(&b)) {
return -1;
}
if (b) {
return 0;
}
return -1;
}
/* This one is similar to accept4 and has to return a valid FD of type socket */
int __wrap_socket(int domain, int type, int protocol) {
/* Only accept valid families */
if (domain != AF_INET && domain != AF_INET6) {
return -1;
}
int fd = allocate_fd();
if (fd != -1) {
struct socket_file *sf = (struct socket_file *)malloc(sizeof(struct socket_file));
/* Init the file */
init_fd(fd, FD_TYPE_SOCKET, (struct file *)sf);
}
#ifdef PRINTF_DEBUG
printf("socket returning fd: %d\n", fd);
#endif
return fd;
}
int __wrap_shutdown() {
//printf("Wrapped shutdown\n");
return 0;
}
/* The timerfd syscalls */
struct timer_file {
struct file base;
};
int __wrap_timerfd_create(int clockid, int flags) {
int fd = allocate_fd();
if (fd != -1) {
struct timer_file *tf = (struct timer_file *)malloc(sizeof(struct timer_file));
/* Init the file */
init_fd(fd, FD_TYPE_TIMER, (struct file *)tf);
}
#ifdef PRINTF_DEBUG
printf("timerfd_create returning fd: %d\n", fd);
#endif
return fd;
}
int __wrap_timerfd_settime(int fd, int flags,
const struct itimerspec *new_value,
struct itimerspec *old_value) {
//printf("timerfd_settime: %d\n", fd);
return 0;
}
/* The eventfd syscalls */
struct event_file {
struct file base;
};
int __wrap_eventfd() {
int fd = allocate_fd();
if (fd != -1) {
struct event_file *ef = (struct event_file *)malloc(sizeof(struct event_file));
/* Init the file */
init_fd(fd, FD_TYPE_EVENT, (struct file *)ef);
//printf("eventfd: %d\n", fd);
}
#ifdef PRINTF_DEBUG
printf("eventfd returning fd: %d\n", fd);
#endif
return fd;
}
// timerfd_settime
/* File descriptors exist in a shared dimension, and has to know its type */
extern int __real_close(int fd);
int __wrap_close(int fd) {
if (fd < RESERVED_SYSTEM_FDS) {
return __real_close(fd);
}
struct file *f = map_fd(fd);
if (!f) {
return -1;
}
if (f->type == FD_TYPE_EPOLL) {
#ifdef PRINTF_DEBUG
printf("Closing epoll FD: %d\n", fd);
#endif
free(f);
return free_fd(fd);
} else if (f->type == FD_TYPE_TIMER) {
#ifdef PRINTF_DEBUG
printf("Closing timer fd: %d\n", fd);
#endif
free(f);
return free_fd(fd);
} else if (f->type == FD_TYPE_EVENT) {
#ifdef PRINTF_DEBUG
printf("Closing event fd: %d\n", fd);
#endif
free(f);
return free_fd(fd);
} else if (f->type == FD_TYPE_SOCKET) {
#ifdef PRINTF_DEBUG
printf("Closing socket fd: %d\n", fd);
#endif
// we should call epoll_ctl remove here
free(f);
int ret = free_fd(fd);
#ifdef PRINTF_DEBUG
printf("Ret: %d\n", ret);
#endif
//free(-1);
return ret;
}
return -1;
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
set_consumable_data(data, size);
test();
if (num_fds) {
printf("ERROR! Cannot leave open FDs after test!\n");
}
return 0;
}
#ifdef __cplusplus
}
#endif

View File

@@ -1,145 +0,0 @@
/* We need to include the APIs to fuzz within */
#include "epoll_fuzzer.h"
/* Our test fuzz uSockets so we need its APIs */
#include "bun-sockets/src/libusockets.h"
int num_open_sockets = 0;
void wakeup_cb(struct us_loop_t *loop) {
}
void pre_cb(struct us_loop_t *loop) {
}
void post_cb(struct us_loop_t *loop) {
}
struct us_socket_t *on_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) {
num_open_sockets++;
return s;
}
struct us_socket_t *on_close(struct us_socket_t *s, int code, void *reason) {
num_open_sockets--;
return s;
}
struct us_socket_t *on_data(struct us_socket_t *s, char *data, int length) {
//exit(33);
return s;
}
struct us_socket_t *on_end(struct us_socket_t *s) {
return s;
}
struct us_listen_socket_t *listen_socket;
#include "App.h"
/* We define a test that deterministically sets up and tears down an uSockets event-loop */
void test() {
//printf("Entering test\n");
/*struct us_loop_t *loop = us_create_loop(0, wakeup_cb, pre_cb, post_cb, 0);
struct us_socket_context_options_t context_options = {};
struct us_socket_context_t *context = us_create_socket_context(0, loop, 0, context_options);
us_socket_context_on_open(0, context, on_open);
us_socket_context_on_close(0, context, on_close);
us_socket_context_on_data(0, context, on_data);
us_socket_context_on_end(0, context, on_end);
listen_socket = us_socket_context_listen(0, context, 0, 3001, 0, 0);
if (listen_socket) {
printf("We are listening!\n");
} else {
printf("Failed to listen!\n");
}
us_loop_run(loop);
us_socket_context_free(0, context);
us_loop_free(loop);*/
// skapa event-loopen explicit?
//struct us_loop_t *loop = us_create_loop(0, wakeup_cb, pre_cb, post_cb, 0);
// testing hello world http
/*struct us_socket_context_options_t context_options = {};
uWS::App(context_options).get("/*", [](auto *res, auto *req) {
res->end("Hello world!");
}).listen(3000, [](auto *listenSocket) {
listen_socket = listenSocket;
}).run();*/
/* 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::SHARED_COMPRESSOR,
.maxPayloadLength = 16 * 1024,
.idleTimeout = 10,
.maxBackpressure = 1 * 1024 * 1024,
/* Handlers */
.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) {
ws->send(message, opCode, true);
},
.drain = [](auto *ws) {
/* Check ws->getBufferedAmount() here */
},
.ping = [](auto *ws) {
/* Not implemented yet */
},
.pong = [](auto *ws) {
/* 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;
}).run();
uWS::Loop::get()->free();
//printf("Leaving test\n");
}
/* 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
us_listen_socket_close(0, listen_socket);
listen_socket = NULL;
}

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