Skip to content
openpeepsPublic

About

💥 A high-performance event notification library. UDP, TCP, HTTP/1.1, HTTP/2 and WebSockets. Alternative to libuv and libevent

Topics

Resources

Stars

22 stars

Watchers

1 watching

Forks

Latest commit

 

History

295 Commits

Folders and files

Repository files navigation


PowPow 💥 A high-performance event notification library for Nim

nimble install powpow

API reference
Github Actions Github Actions

😍 Key Features

  • High-performance, event-driven networking library for Nim
  • Support for low-level UDP, TCP sockets
  • Built-in HTTP/1.1 & HTTP/2 server implementation
  • Built-in WebSocket client and server: standalone + HTTP-upgrade modes
  • TLS/SSL support (implicit + STARTTLS-style upgrades)
  • DTLS 1.2 over UDP — one socket, per-peer sessions, stateless cookie exchange
  • Threadpool: a self-managed worker pool. Submit CPU-bound or blocking work off a live event loop without blocking any single thread
  • Signal/Relay system for in-process event dispatch
  • Built-in rate limiting per client IP with multi-window support
  • HTTP over Unix Domain Sockets (UDS) support for super fast local IPC
  • Zero-copy file transmission using sendfile (Unix) and TransmitFile (Windows)
  • Chunked Request Body support for streaming uploads and large payloads
  • Memory-efficient Multipart Form Data parsing and Raw Body handling for file uploads
  • SIMD-accelerated parsing and formatting of HTTP messages
  • Built on top of epoll (Linux), and kqueue (BSD, macOS), IOCP (Windows)
  • Opt-in Linux io_uring backend (submission-based, --features:io_uring) with zero-copy send_zc sends, SPLICE file transfers, registered buffers and a full io_uring API binding — documentation
  • Support for edge-triggered and level-triggered event notification
  • Support for multiple event loops and multi-threaded applications
  • Support for MIME type detection based on file extensions
  • FileSystem Monitoring via inotify (Linux) and kqueue (BSD, macOS) (Windows - not yet implemented)

Note

💥 PowPow is now available in Supranim as a backend. Just switch --features:powpow when compiling your Supranim app!

Warning

💥 This library is not production-ready and may contain bugs and security vulnerabilities. It has been tested on Linux and macOS, but may not work on all platforms. Use it, test it, and do not hesitate to report any issues you find! 💥

Note

io_uring expectations. On plain HTTP/1.x, request/response cycles over TCP are serialized per connection, so io_uring doesn't dramatically outpace epoll on a single-connection benchmark — it's a way to reach parity with headroom as concurrency scales and for zero-copy file serving. See the performance docs for details. Experimental HTTP/2 multiplexing (see docs/contents/http/http2.md) is where that parallelism counts; QUIC is still future work.

📚 Documentation

The full documentation lives in docs/:

Security

Need to take input validation and DoS-resistance seriously. A dedicated security audit was performed; the high-priority findings it produced are fixed and covered by regression tests in tests/test_security.nim.

Recommended production configuration

For publicly reachable endpoints, set explicit caps instead of relying on the defaults (the maxBodySize = 0 backstop is MaxStreamBodySize:

let server = newHttpServer(loop)
server.maxBodySize    = 50 * 1024 * 1024      # 50 MB total request body
server.maxStreamBodySize = 64 * 1024 * 1024   # hard cap even when maxBodySize=0
server.maxFileSize    = 10 * 1024 * 1024      # 10 MB per uploaded file
server.maxFieldSize   = 64 * 1024             # 64 KB per text field
server.maxConnections = 4096                  # cap concurrent connections
server.maxPipelineDepth = 4                   # cap pipelined requests
server.readTimeoutMs  = 5_000                 # slowloris / partial-request close
server.setKeepAliveTimeout(5_000)             # idle keep-alive close

The same caps apply to the standalone WsServer via maxFrameSize, handshakeTimeoutMs, maxHandshakeSessions, and (once enabled) a post-upgrade idleTimeoutMs.

Smuggler is a grammar-based HTTP/1.x request-smuggling fuzzer built for this library: it generates and mutates requests from a context-free grammar, detects CL/TE desyncs with an in-process oracle, and drives live servers with the two-request response-pairing technique.

Threadpool

Use the threadpool when you have CPU-bound or blocking work that must not stall an event loop. It creates N persistent worker threads plus one dispatch thread running a private event loop. Job results are delivered as callbacks serialized on that dispatch thread, so no user-side locking is needed.

import powpow/threadpool

let tp = newThreadPool(size = 4)

discard tp.submitWork(
  job = proc(): string = readFile("big.bin"),
  cb  = proc(data: string) = echo "read ", data.len, " bytes")

closeThreadPool(tp)   # drain queued jobs, then tear down

shutdownThreadPool(tp) discards queued-but-unstarted jobs for fast teardown; in-flight jobs still finish and deliver. Both are idempotent.

Unlike std/threadpool (deprecated), taskpools or malebolgia, powpow's threadpool never blocks the calling thread, making it safe inside event-driven services. Work-stealing forks like weave or taskpools still win for numeric crunching; powpow's pool optimizes for service workloads where results must flow back into an event loop. See the concurrency guide for the full comparison.

Examples (the fun part)

Most web servers out there are all rainbows and flowers, until you upload or stream a file, and it transforms into a nightmare at runtime. PowPow is slowly moving toward a production-ready server. Everything below is runnable and lives in the examples/ directory.

Check runnable examples 👇
  • httpserver.nim the classic. A tiny, functional HTTP/1.1 server

  • httpserver_threads.nim the same server, but it spawns one event loop per CPU core and binds them all to the same port via SO_REUSEPORT. The kernel load-balances connections across workers for you

  • http2server.nim the same idea over HTTP/2: multiplexed streams on one TCP connection. Serves h2 over TLS by default with an embedded self-signed cert, so you can open https://localhost:9040/ straight in a browser (accept the warning); --h2c for cleartext (curl --http2-prior-knowledge http://localhost:9040/hello), --tls cert.pem key.pem for your own cert

  • upload_server.nim file uploads done right, using pkg/multipart two ways:

    • /upload/raw raw body streamed straight to disk via streamToFile()
    • /upload/stream multipart parsed on the fly with getMultipart()
    • Both keep RAM low and your hard drive honest. Runnable example: upload_server.nim
  • stream_server.nim, it streams and serves a 2.76 GB Big_Buck_Bunny_4K.webm (get it from here > https://en.wikipedia.org/wiki/File:Big_Buck_Bunny_4K.webm) with three different APIs:

    • /video zero-copy media streaming with chunk limiting (1 MB per response), always keep-alive, always Range-aware
    • /download Content-Disposition: attachment, optional Range support
    • /resume full serveFile with If-None-Match, If-Modified-Since, If-Range and Range handling, 304/206 and all. Resume support built in, because your users will close the laptop lid mid-download
  • wsserver.nim a standalone WebSocket server. The upgrade handshake is handled internally; there are no HTTP routes at all

  • wsclient.nim the other side of the socket: newWsClient connects to ws://127.0.0.1:9001, sends text and binary via one sendMessage API, and auto-reconnects with exponential backoff if the server dies mid-session. Kill wsserver.nim while it runs and watch it claw its way back

  • wsupgrade.nim HTTP and WebSocket on the same port. curl localhost:9000/ for HTML, websocat ws://localhost:9000/ws for real-time. One process, one port, two protocols. The browser test page (wsclient.html) is included so you can watch it work live

  • ratelimit_server.nim built-in sliding-window rate limiting per client IP

  • fswatch.nim file system monitoring via the same event loop (inotify on Linux, kqueue on macOS/BSD)

  • tcp_chat.nim a real multi-client chat room on the raw TCP layer, no HTTP in sight. The server broadcasts every client's bytes to everyone else; nc 127.0.0.1 9010 and start arguing with yourself in two terminals

  • tcp_client.nim the chat's better half an interactive stdin client for tcp_chat.nim. Stdin is polled non-blockingly on the loop, so replies print while you are still typing

  • tcp_proxy.nim a TCP reverse proxy / load balancer: it accepts clients on :9020, opens an upstream connection to a backend on :9021, and pipes bytes both ways, buffering anything that arrives before the upstream is ready. nc 127.0.0.1 9020, type, watch the backend echo come back

  • udp_echo.nim UDP done politely: a bound socket that echoes every datagram back to its sender (bindUdp + sendTo), plus a --client mode that pings the server with connectUdp. nc -u works too

  • static_server.nim a static site server: serveStatic from examples/www/ (zero-copy sendFile, path-traversal and symlink-escape safe), CORS headers on everything, and a tiny /api/time JSON endpoint. A whole website, served from one process and a folder of files

  • uds_server.nim HTTP over a Unix domain socket, no TCP stack involved. The whole request stays on the machine, which is great if you and your microservice have agreed to never speak over the network again. curl --unix-socket /tmp/powpow.sock http://localhost/hello

  • tls_server.nim an HTTPS server with an embedded self-signed certificate. curl -k https://localhost:9443/hello and the TLS handshake happens before your coffee does

  • httpserver_ktls.nim the same idea with kernel TLS: sudo modprobe tls, then file bodies leave through a real sendfile(2) onto the TLS socket and never enter userspace. curl -k https://localhost:9444/ktls reports whether this connection actually offloaded. Falls back to ordinary userspace TLS wherever kTLS doesn't exist, with no branching in your handler

  • stream_server_ktls.nim the streaming/download routes of stream_server.nim over TLS — streamFile, sendFile, resume-aware serveFile — showing which of them actually reach the zero-copy path and which never would

  • signal_bus.nim an in-process pub/sub event bus (SignalRelay): an HTTP endpoint emits named events and subscribers react to them, including listenOnce and manual unlisten. Server-side events without a server-side framework

  • timers_scheduler.nim a guided tour of the timer wheel: one-shot timers, repeating intervals, deferred callbacks, and idle handlers, all ticking on the same loop for ~8 seconds before politely stopping

  • ws_chat.nim a multi-client WebSocket chat with broadcast. Open http://localhost:9006 in two browser tabs, type in one, and enjoy the other one agreeing with you. The browser page lives in ws_chat.html

Dummy Benchmarks

Pow Pow is the #1 fastest HTTP server from Web Framework Benchmarks. Find the wrk-based benchmark I manually ran via Github Actions (see bench.yml)

  • Single-threaded (keep-alive)
💥 powpow HTTP server listening on http://localhost:9000
  Press Ctrl+C to stop
Running 5s test @ http://127.0.0.1:9000/
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.02ms   61.67us   3.25ms   81.86%
    Req/Sec    24.63k   818.23    26.67k    68.00%
  489946 requests in 5.00s, 277.55MB read
Requests/sec:  97965.24
Transfer/sec:     55.50MB
  • Single-threaded (connection close)
💥 powpow HTTP server listening on http://localhost:9000
  Press Ctrl+C to stop
Running 5s test @ http://127.0.0.1:9000/
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.97ms  202.12us   4.22ms   80.09%
    Req/Sec     9.13k     3.62k   18.16k    71.43%
  184469 requests in 5.10s, 103.62MB read
Requests/sec:  36174.22
Transfer/sec:     20.32MB
  • Multi-threaded (keep-alive)
  worker #0 ready
  worker #2 ready
💥 powpow accepting on 0.0.0.0:9000 with 4 workers (SO_REUSEPORT)
  worker #1 ready
  worker #3 ready
Running 5s test @ http://127.0.0.1:9000/
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   786.79us    1.11ms  11.73ms   84.24%
    Req/Sec    50.98k     3.97k   86.53k    91.50%
  1018820 requests in 5.03s, 526.62MB read
Requests/sec: 202743.27
Transfer/sec:    104.80MB

  • Multi-threaded (connection close)
  worker #0 ready
  worker #2 ready
💥 powpow accepting on 0.0.0.0:9000 with 4 workers (SO_REUSEPORT)
  worker #3 ready
  worker #1 ready
Running 5s test @ http://127.0.0.1:9000/
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.31ms    0.95ms  15.44ms   67.66%
    Req/Sec    13.24k   614.80    14.91k    81.50%
  264133 requests in 5.01s, 135.27MB read
Requests/sec:  52687.29
Transfer/sec:     26.98MB

  • Event loop concurrency (macOS kqueue, release build)

    Capacity scaling:

    N throughput p50 p99
    100 613k/s 43µs 79µs
    500 649k/s 193µs 382µs
    1k 534k/s 451µs 862µs
    2k 539k/s 854µs 1.7ms
    4k 486k/s 2.0ms 4.0ms
    8k 439k/s 4.7ms 9.0ms
    12k 473k/s 6.5ms 12.4ms

    Head-of-line blocking (1024 fast + 64 slow at 200µs):

    p50 p99
    baseline (0 slow) 421µs 830µs
    with 64 slow 415µs 820µs
    penalty ~0µs ~0µs
  • Event loop concurrency (Linux epoll, release build)

    Capacity scaling:

    N throughput p50 p99
    100 307k/s 100µs 198µs
    500 304k/s 504µs 1.0ms
    1k 277k/s 1.0ms 2.1ms
    2k 252k/s 2.4ms 4.8ms
    4k 231k/s 5.2ms 10.2ms
    8k 233k/s 10.5ms 20.5ms
    12k 231k/s 15.8ms 30.6ms

    Head-of-line blocking (1024 fast + 64 slow at 200µs):

    p50 p99
    baseline (0 slow) 1.1ms 2.2ms
    with 64 slow 1.3ms 2.3ms
    penalty ~146µs ~164µs
  • Event loop concurrency (Linux io_uring, release build)

    Capacity scaling:

    N throughput p50 p99
    100 255k/s 100µs 209µs
    500 254k/s 554µs 1.1ms
    1k 232k/s 1.1ms 2.1ms
    2k 220k/s 2.5ms 4.9ms
    4k 206k/s 5.4ms 10.5ms
    8k 208k/s 10.5ms 23.8ms
    12k 195k/s 21.2ms 40.7ms

    Head-of-line blocking (1024 fast + 64 slow at 200µs):

    p50 p99
    baseline (0 slow) 1.3ms 2.5ms
    with 64 slow 1.2ms 2.3ms
    penalty ~0µs ~0µs

Security roadmap

  • Coverage-guided fuzzing of the HTTP / WebSocket / multipart parsers (libFuzzer & nim-drchaos adapters in smuggler)
  • ASan/UBSan sanitizer build wired into CI
  • Stream body bytes before first-packet buffering (avoid peak RAM on large single-packet uploads)
  • Multipart per-file size limits wired to server configuration
  • Symlink-safe static serving (realpath checks)
  • WebSocket handshake timeout and handshake-session bound
  • Rate-limiter thread-safety in multi-threaded mode
  • Strict header parsing (reject obs-fold/leading-whitespace header lines, non-chunked Transfer-Encoding tokens)
  • Response-reflection guards for large attacker-controlled echoes under TLS

Projects built on top of PowPow

  • Supranim - A full-featured web framework in Nim
  • Whiz - A message queue library implementing ZMTP 3.0 in Nim
  • NSSH - A pure Nim SSH server and client based on PowPow
  • RTMP - A pure Nim RTMP server and client on top of PowPow
  • GrooveBox - Badass Live Streaming Straight from Your Disk. RTMP Client & Server + Icecast Client & Server
  • StupidGreen - A static site generator with themes support (made with Supranim & PowPow backend)
  • Booyaka - A documentation generator based on Markdown + Supranim + PowPow

❤ Contributions & Support

🎩 License

MIT license. Made by Humans from OpenPeeps.
Copyright OpenPeeps & Contributors — All rights reserved.

About

💥 A high-performance event notification library. UDP, TCP, HTTP/1.1, HTTP/2 and WebSockets. Alternative to libuv and libevent

Topics

Resources

Stars

22 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

Generated from openpeeps/pistachio