Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
896 changes: 896 additions & 0 deletions frameworks/proxygen-coro/ArenaCoroServer.cpp

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions frameworks/proxygen-coro/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
cmake_minimum_required(VERSION 3.20)

project(httparena-proxygen-coro LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Proxygen's exported Fizz package calls find_dependency(Sodium). The official
# builder image keeps that upstream find module with the Proxygen source tree.
list(APPEND CMAKE_MODULE_PATH "/proxygen/build/fbcode_builder/CMake")

# The official image exports c-ares as a static archive but some transitive
# Proxygen targets refer to the un-namespaced CMake target.
add_library(cares STATIC IMPORTED GLOBAL)
set_target_properties(
cares
PROPERTIES
IMPORTED_LOCATION "/opt/proxygen/lib/libcares.a"
INTERFACE_INCLUDE_DIRECTORIES "/opt/proxygen/include"
)

find_package(proxygen CONFIG REQUIRED)

add_executable(proxygen-arena-coro ArenaCoroServer.cpp)
target_compile_options(proxygen-arena-coro PRIVATE -Wall -Wextra -Wpedantic)
target_link_libraries(
proxygen-arena-coro
PRIVATE
proxygen::proxygen
proxygen::proxygen_coro
proxygen::proxygen_coro_server
proxygen::proxygen_http_coro_filters_compression_filter_factory
Folly::folly_init_init
Folly::folly_portability_gflags
)
33 changes: 33 additions & 0 deletions frameworks/proxygen-coro/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM ghcr.io/facebook/proxygen/base:latest AS build

WORKDIR /arena
COPY CMakeLists.txt ArenaCoroServer.cpp ./
RUN cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \
&& cmake --build build --parallel "$(nproc)" \
&& strip build/proxygen-arena-coro

RUN set -eux; \
ldd build/proxygen-arena-coro \
| awk '/=> \// { print $3 } /^\// { print $1 }' | sort -u > /tmp/runtime-libs.txt; \
tar -chf /tmp/runtime-libs.tar --files-from=/tmp/runtime-libs.txt

FROM ubuntu:24.04@sha256:019e8eb29a85e74d64925745884f2ec79aa27e3feab36353d24656f4d6b89467

ENV LD_LIBRARY_PATH=/opt/proxygen/lib

COPY --from=build /tmp/runtime-libs.tar /tmp/runtime-libs.tar
RUN tar -xf /tmp/runtime-libs.tar -C / \
&& rm /tmp/runtime-libs.tar

COPY --from=build /arena/build/proxygen-arena-coro /usr/local/bin/proxygen-arena-coro
COPY entrypoint.sh /usr/local/bin/proxygen-coro-entrypoint

RUN chmod +x /usr/local/bin/proxygen-coro-entrypoint \
&& groupadd --system --gid 10001 httparena \
&& useradd --system --uid 10001 --gid httparena --no-create-home \
--home-dir /nonexistent --shell /usr/sbin/nologin httparena

EXPOSE 8080/tcp 8081/tcp 8082/tcp 8443/tcp 8443/udp

USER httparena
ENTRYPOINT ["/usr/local/bin/proxygen-coro-entrypoint"]
52 changes: 52 additions & 0 deletions frameworks/proxygen-coro/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# proxygen-coro

This engine exercises Proxygen's native coroutine server stack rather than the
callback `RequestHandler` / `HTTPTransactionHandler` APIs used by the regular
`proxygen` entry.

`ArenaCoroServer.cpp` implements `proxygen::coro::HTTPHandler`, consumes
requests through `HTTPSourceHolder`, and returns `HTTPFixedSource` responses.
Uploads are counted while asynchronously draining BODY events. WebSockets use
a long-lived custom `HTTPSource`: its response calls
`setEgressWebsocketUpgrade()`, then parses and emits RFC 6455 frames over the
raw upgraded BODY event stream. Response compression is provided by the coro
`ServerCompressionFilterFactory`.

## Listener layout

The entrypoint starts five instances of the same binary because one coro
`HTTPServer` instance selects either TCP or QUIC transport:

- `8080/tcp`: HTTP/1.1 and WebSockets
- `8081/tcp`: HTTP/1.1 over TLS, ALPN `http/1.1`
- `8082/tcp`: prior-knowledge h2c
- `8443/tcp`: HTTP/2 over TLS, ALPN `h2`
- `8443/udp`: HTTP/3 over QUIC, ALPN `h3`

By default each listener uses Proxygen's available-CPU thread count. Benchmark
profiles exercise one listener at a time, while the unused listener pools
sleep, so the active protocol can use the full machine. Override the
per-listener count with `PROXYGEN_CORO_THREADS`.

The coroutine session uses a 32 MiB stream flow-control window and a 128 MiB
connection window. The QUIC listener mirrors Proxygen's interop tuning with
107374182-byte advertised connection and stream windows, BBR congestion
control, pacing enabled, and a 200 microsecond pacing tick.

## Upstream image and build

The Docker build tracks the official
`ghcr.io/facebook/proxygen/base:latest` builder image. The runtime stage copies
only the compiled binary and its dynamically linked libraries into a pinned
Ubuntu 24.04 image, then runs as the non-root `httparena` user (UID/GID 10001).

From the repository root:

```bash
./scripts/validate.sh proxygen-coro
./scripts/run.sh proxygen-coro
```

The implementation follows the upstream coroutine echo server and the
`H12DownstreamSessionTest.WebSocketUpgrade` test, which documents upgraded
raw bytes flowing through coroutine BODY events.
43 changes: 43 additions & 0 deletions frameworks/proxygen-coro/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
set -euo pipefail

THREADS=${PROXYGEN_CORO_THREADS:-0}
PIDS=()

shutdown() {
trap - TERM INT EXIT
for pid in "${PIDS[@]}"; do
kill -TERM "$pid" 2>/dev/null || true
done
for pid in "${PIDS[@]}"; do
wait "$pid" 2>/dev/null || true
done
}

trap shutdown TERM INT EXIT

start_server() {
/usr/local/bin/proxygen-arena-coro \
--ip=:: \
--cert=/certs/server.crt \
--key=/certs/server.key \
--threads="$THREADS" \
"$@" &
PIDS+=("$!")
}

# A coro HTTPServer instance is either TCP or QUIC, so TCP H2 and UDP H3 use
# separate processes while sharing the numeric 8443 port.
start_server --port=8080 --plaintext_protocol=http/1.1
start_server --port=8081 --plaintext_protocol=http/1.1 --tls=true --alpn=http/1.1
start_server --port=8082 --plaintext_protocol=h2c
start_server --port=8443 --tls=true --alpn=h2
start_server --port=8443 --tls=true --quic=true --alpn=h3

set +e
wait -n "${PIDS[@]}"
STATUS=$?
set -e

shutdown
exit "$STATUS"
30 changes: 30 additions & 0 deletions frameworks/proxygen-coro/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"display_name": "proxygen-coro",
"language": "C++",
"type": "engine",
"engine": "proxygen-coro",
"description": "Meta's Proxygen native coroutine HTTPServer and HTTPSource APIs across HTTP/1.1, HTTP/1.1 TLS, h2c, HTTP/2 TLS, HTTP/3 QUIC, and RFC 6455 WebSockets.",
"repo": "https://github.com/facebook/proxygen",
"enabled": true,
"tests": [
"baseline",
"json",
"json-comp",
"json-tls",
"upload",
"static",
"static-tls",
"pipelined",
"limited-conn",
"baseline-h2",
"baseline-h2c",
"json-h2c",
"static-h2",
"baseline-h3",
"static-h3",
"echo-ws",
"echo-ws-pipeline",
"echo-ws-limited"
],
"maintainers": []
}
Loading