test_reverse_proxy.sh (22113B)
1 #!/bin/bash 2 # 3 # Reverse-proxy integration tests for paivana. 4 # 5 # Starts upstream HTTP servers (one per language: C/MHD, Go, Python, Rust) 6 # and a paivana-httpd instance running with -n (paywall disabled), then 7 # exercises the reverse-proxy behaviors with curl, wget, and a custom 8 # libcurl / raw-socket pipelining client. 9 # 10 # Progress markers: each test prints "<description> " with no newline, 11 # then "OK" on pass or "FAIL: <detail>" on failure. Failure exits 12 # non-zero, which make(1) treats as a TEST FAILURE. 13 # 14 # Environment variables honored: 15 # PAIVANA_HTTPD path to paivana-httpd binary (default: built 16 # in the sibling src/backend tree) 17 # SRCDIR source dir containing .py / .rs / .go sources 18 # (default: directory of this script) 19 # BUILDDIR directory holding upstream_mhd, upstream_go, 20 # upstream_rs, pipeline_client (default: $PWD) 21 # KEEP_TMP=1 keep the scratch dir and log files after exit 22 # 23 set -u 24 25 function die() { 26 echo "FAIL: $*" >&2 27 exit 1 28 } 29 30 function msg() { 31 printf '%s ' "$*" 32 } 33 34 function ok() { 35 echo "OK" 36 } 37 38 function fail() { 39 echo "FAIL: $*" 40 dump_logs 41 exit 1 42 } 43 44 function here() { 45 cd -- "$(dirname -- "$0")" && pwd 46 } 47 48 SRCDIR="${SRCDIR:-$(here)}" 49 BUILDDIR="${BUILDDIR:-$PWD}" 50 51 # Default to the in-tree build path. 52 PAIVANA_HTTPD="${PAIVANA_HTTPD:-$BUILDDIR/../backend/paivana-httpd}" 53 if [ ! -x "$PAIVANA_HTTPD" ]; 54 then 55 # Try the source layout (e.g. when tests are run from source tree) 56 alt="$SRCDIR/../backend/paivana-httpd" 57 if [ -x "$alt" ]; 58 then 59 PAIVANA_HTTPD="$alt" 60 fi 61 fi 62 63 if [ ! -x "$PAIVANA_HTTPD" ]; 64 then 65 echo "SKIP: paivana-httpd binary not found (looked at $PAIVANA_HTTPD)" >&2 66 exit 77 67 fi 68 69 # Binaries/commands for upstreams 70 UPSTREAM_MHD="$BUILDDIR/upstream_mhd" 71 UPSTREAM_GO="$BUILDDIR/upstream_go" 72 UPSTREAM_RS="$BUILDDIR/upstream_rs" 73 PIPELINE_CLIENT="$BUILDDIR/pipeline_client" 74 75 # Ports (fixed, but we still wait/retry binding; if they're busy the 76 # test bails out early so the user can rerun.) 77 PAIVANA_PORT=18500 78 MHD_PORT=18401 79 GO_PORT=18402 80 PY_PORT=18403 81 RS_PORT=18404 82 DEAD_PORT=18499 # nothing should be listening here 83 84 TMPDIR="$(mktemp -d -t paivana-tests.XXXXXX)" 85 LOGDIR="$TMPDIR/logs" 86 mkdir -p "$LOGDIR" 87 88 # paivana normally resolves its config.d via the install prefix. 89 # Tests run against the uninstalled build tree, so point the 90 # project's base-config override at an empty directory: no 91 # auxiliary config snippets are needed for reverse-proxy tests. 92 BASE_CONFIG_DIR="$TMPDIR/configd" 93 mkdir -p "$BASE_CONFIG_DIR" 94 export PAIVANA_BASE_CONFIG="$BASE_CONFIG_DIR" 95 96 PIDS=() 97 PAIVANA_PID="" 98 99 function dump_logs() { 100 echo "-- logs in $LOGDIR --" >&2 101 for f in "$LOGDIR"/*.log; do 102 [ -e "$f" ] || continue 103 echo "==> $f <==" >&2 104 tail -n 40 "$f" >&2 105 done 106 } 107 108 function cleanup() { 109 set +e 110 for p in "${PIDS[@]:-}"; 111 do 112 [ -n "$p" ] && kill -TERM "$p" 2>/dev/null 113 done 114 [ -n "$PAIVANA_PID" ] && kill -TERM "$PAIVANA_PID" 2>/dev/null 115 # Give them a moment to exit cleanly 116 sleep 0.2 117 for p in "${PIDS[@]:-}"; 118 do 119 [ -n "$p" ] && kill -KILL "$p" 2>/dev/null 120 done 121 [ -n "$PAIVANA_PID" ] && kill -KILL "$PAIVANA_PID" 2>/dev/null 122 if [ "${KEEP_TMP:-0}" = "1" ]; 123 then 124 echo "Temp files kept in $TMPDIR" >&2 125 else 126 rm -rf "$TMPDIR" 127 fi 128 } 129 trap cleanup EXIT 130 trap 'echo "FAIL: interrupted" >&2; exit 1' INT TERM 131 132 function wait_for_port() { 133 # Block until a TCP port accepts a connection (max ~5s). 134 # NOTE: must run the /dev/tcp probe in a subshell — `exec` on the 135 # parent shell with a failing redirection would terminate bash in 136 # non-interactive mode (the 2>/dev/null does not suppress that). 137 local host="$1" port="$2" tries=50 138 while [ "$tries" -gt 0 ]; 139 do 140 if ( exec 7<>"/dev/tcp/$host/$port" ) 2>/dev/null; 141 then 142 return 0 143 fi 144 sleep 0.1 145 tries=$((tries - 1)) 146 done 147 return 1 148 } 149 150 # Start a background upstream; record pid in PIDS. 151 function start_bg() { 152 local name="$1" port="$2"; shift 2 153 local log="$LOGDIR/$name.log" 154 ( exec "$@" "$port" ) >"$log" 2>&1 & 155 local pid=$! 156 PIDS+=("$pid") 157 if ! wait_for_port 127.0.0.1 "$port"; 158 then 159 echo "FAIL: $name did not start on port $port" >&2 160 tail -n 20 "$log" >&2 161 exit 1 162 fi 163 } 164 165 function start_paivana() { 166 # $1 = upstream base URL 167 local dest="$1" 168 local cfg="$TMPDIR/paivana.conf" 169 sed -e "s|@DEST@|$dest|g" -e "s|@PORT@|$PAIVANA_PORT|g" \ 170 "$SRCDIR/test_reverse_proxy.conf.in" > "$cfg" 171 local log="$LOGDIR/paivana.log" 172 ( exec "$PAIVANA_HTTPD" -c "$cfg" -n -L WARNING ) >"$log" 2>&1 & 173 PAIVANA_PID=$! 174 if ! wait_for_port 127.0.0.1 "$PAIVANA_PORT"; 175 then 176 echo "FAIL: paivana-httpd did not start on port $PAIVANA_PORT" >&2 177 tail -n 20 "$log" >&2 178 exit 1 179 fi 180 } 181 182 function stop_paivana() { 183 if [ -n "$PAIVANA_PID" ]; 184 then 185 kill -TERM "$PAIVANA_PID" 2>/dev/null 186 wait "$PAIVANA_PID" 2>/dev/null 187 PAIVANA_PID="" 188 fi 189 } 190 191 # Start all upstreams that we have binaries for. 192 function start_upstreams() { 193 start_bg mhd "$MHD_PORT" "$UPSTREAM_MHD" 194 if [ -x "$UPSTREAM_GO" ]; 195 then 196 start_bg go "$GO_PORT" "$UPSTREAM_GO" 197 else 198 echo "NOTE: upstream_go not built, skipping Go upstream tests" >&2 199 GO_PORT="" 200 fi 201 if [ -x "$UPSTREAM_RS" ]; 202 then 203 start_bg rs "$RS_PORT" "$UPSTREAM_RS" 204 else 205 echo "NOTE: upstream_rs not built, skipping Rust upstream tests" >&2 206 RS_PORT="" 207 fi 208 if command -v python3 >/dev/null 2>&1; 209 then 210 local log="$LOGDIR/py.log" 211 ( exec python3 "$SRCDIR/upstream_py.py" "$PY_PORT" ) >"$log" 2>&1 & 212 PIDS+=("$!") 213 if ! wait_for_port 127.0.0.1 "$PY_PORT"; 214 then 215 echo "FAIL: upstream_py did not start on port $PY_PORT" >&2 216 tail -n 20 "$log" >&2 217 exit 1 218 fi 219 else 220 echo "NOTE: python3 not available, skipping Python upstream tests" >&2 221 PY_PORT="" 222 fi 223 } 224 225 ###################################################################### 226 # Test helpers 227 ###################################################################### 228 229 PAIVANA_URL() { echo "http://127.0.0.1:$PAIVANA_PORT$1"; } 230 231 # GET via curl; verifies status code and a substring of the body. 232 function test_get() { 233 local desc="$1" path="$2" want_status="$3" want_sub="$4" 234 msg "$desc" 235 local out status 236 out="$(curl -sS -o "$TMPDIR/body" -w '%{http_code}' "$(PAIVANA_URL "$path")" 2>"$TMPDIR/err")" \ 237 || { fail "curl: $(cat "$TMPDIR/err")"; } 238 status="$out" 239 [ "$status" = "$want_status" ] || fail "status=$status want=$want_status" 240 if [ -n "$want_sub" ] && ! grep -q -- "$want_sub" "$TMPDIR/body"; 241 then 242 fail "body missing substring '$want_sub' (got: $(tr -d '\n' <"$TMPDIR/body" | head -c 120))" 243 fi 244 ok 245 } 246 247 # HEAD 248 function test_head() { 249 local desc="$1" path="$2" want_status="$3" 250 msg "$desc" 251 local status 252 status="$(curl -sS -I -o /dev/null -w '%{http_code}' "$(PAIVANA_URL "$path")" 2>"$TMPDIR/err")" \ 253 || fail "curl: $(cat "$TMPDIR/err")" 254 [ "$status" = "$want_status" ] || fail "status=$status want=$want_status" 255 ok 256 } 257 258 # Generic method with optional body; checks status and body substring. 259 function test_method() { 260 local desc="$1" method="$2" path="$3" body="$4" want_status="$5" want_sub="$6" 261 msg "$desc" 262 local args=(-sS -o "$TMPDIR/body" -w '%{http_code}' -X "$method" "$(PAIVANA_URL "$path")") 263 if [ -n "$body" ]; 264 then 265 args+=(--data-binary "@$body") 266 fi 267 local status 268 status="$(curl "${args[@]}" 2>"$TMPDIR/err")" \ 269 || fail "curl: $(cat "$TMPDIR/err")" 270 [ "$status" = "$want_status" ] || \ 271 fail "status=$status want=$want_status; body=$(head -c 200 "$TMPDIR/body")" 272 if [ -n "$want_sub" ] && ! grep -q -- "$want_sub" "$TMPDIR/body"; 273 then 274 fail "body missing substring '$want_sub' (got: $(head -c 200 "$TMPDIR/body"))" 275 fi 276 ok 277 } 278 279 ###################################################################### 280 # Test battery — runs against whichever upstream we've pointed 281 # paivana at. 282 ###################################################################### 283 284 function run_battery() { 285 local label="$1" 286 287 test_get "[$label] GET /hello (basic proxy pass-through)" \ 288 /hello 200 "Hello from" 289 290 test_get "[$label] GET /status/201 (2xx status forwarding)" \ 291 /status/201 201 "status 201" 292 293 test_get "[$label] GET /status/404 (4xx status forwarding)" \ 294 /status/404 404 "status 404" 295 296 test_get "[$label] GET /status/500 (5xx status forwarding)" \ 297 /status/500 500 "status 500" 298 299 test_head "[$label] HEAD /hello" /hello 200 300 301 # Medium response body (128 KiB): exercises streaming download 302 test_get "[$label] GET /large/131072 (128 KiB response)" \ 303 /large/131072 200 "" 304 local sz 305 sz="$(wc -c <"$TMPDIR/body" | tr -d ' ')" 306 msg "[$label] verify 128 KiB body length" 307 [ "$sz" = "131072" ] || fail "got $sz bytes, expected 131072" 308 ok 309 310 # POST /echo — round-trip body 311 printf 'hello-payload-%s' "$label" >"$TMPDIR/post_body" 312 test_method "[$label] POST /echo (body round-trip)" \ 313 POST /echo "$TMPDIR/post_body" 200 "hello-payload-$label" 314 315 # POST /upload — byte count 316 dd if=/dev/urandom of="$TMPDIR/rnd" bs=1024 count=64 status=none 317 test_method "[$label] POST /upload (64 KiB binary upload)" \ 318 POST /upload "$TMPDIR/rnd" 200 "Received 65536 bytes" 319 320 # PUT /put 321 test_method "[$label] PUT /put (PUT forwarding)" \ 322 PUT /put "$TMPDIR/post_body" 200 "PUT received" 323 324 # PATCH /patch 325 test_method "[$label] PATCH /patch (PATCH forwarding)" \ 326 PATCH /patch "$TMPDIR/post_body" 200 "PATCH received" 327 328 # DELETE /item/1 with empty body 329 msg "[$label] DELETE /item/1 (204 No Content)" 330 local status 331 status="$(curl -sS -X DELETE -o /dev/null -w '%{http_code}' "$(PAIVANA_URL /item/1)" 2>"$TMPDIR/err")" \ 332 || fail "curl: $(cat "$TMPDIR/err")" 333 [ "$status" = "204" ] || fail "status=$status" 334 ok 335 336 # OPTIONS — server should echo 204 + Allow 337 msg "[$label] OPTIONS /anything (204 + Allow header)" 338 local opts 339 opts="$(curl -sS -X OPTIONS -D "$TMPDIR/hdrs" -o /dev/null -w '%{http_code}' "$(PAIVANA_URL /hello)" 2>"$TMPDIR/err")" \ 340 || fail "curl: $(cat "$TMPDIR/err")" 341 [ "$opts" = "204" ] || fail "status=$opts" 342 grep -qi '^allow:' "$TMPDIR/hdrs" || fail "no Allow header returned" 343 ok 344 345 # Header propagation: X-Forwarded-For must be added by paivana. 346 msg "[$label] GET /echo-headers (X-Forwarded-For added)" 347 curl -sS -o "$TMPDIR/body" "$(PAIVANA_URL /echo-headers)" 2>"$TMPDIR/err" \ 348 || fail "curl: $(cat "$TMPDIR/err")" 349 grep -qi '^x-forwarded-for:' "$TMPDIR/body" || \ 350 fail "upstream did not see X-Forwarded-For; headers:\n$(cat "$TMPDIR/body")" 351 grep -qi '^x-forwarded-proto:' "$TMPDIR/body" || \ 352 fail "upstream did not see X-Forwarded-Proto" 353 grep -qi '^via:' "$TMPDIR/body" || \ 354 fail "upstream did not see Via: paivana" 355 ok 356 357 # RFC 9110 §7.6.3: client's Via chain must be preserved and our 358 # pseudonym *appended* to it, not replaced. 359 msg "[$label] client Via is preserved and paivana is appended" 360 curl -sS -H 'Via: 1.1 alpha.example, 2.0 beta.example' \ 361 -o "$TMPDIR/body" "$(PAIVANA_URL /echo-headers)" 2>"$TMPDIR/err" \ 362 || fail "curl: $(cat "$TMPDIR/err")" 363 local via 364 via="$(grep -i '^via:' "$TMPDIR/body" | tr -d '\r')" 365 [ -n "$via" ] || fail "no Via header at upstream" 366 # Expect: "Via: 1.1 alpha.example, 2.0 beta.example, 1.1 paivana" 367 case "$via" in 368 *"alpha.example"*"beta.example"*paivana*) ;; 369 *) fail "Via not appended correctly: '$via'";; 370 esac 371 ok 372 373 # RFC 9110 §7.6.1: headers named in the client's Connection 374 # header are hop-by-hop and must not be forwarded upstream. 375 msg "[$label] headers named in Connection: are stripped" 376 curl -sS \ 377 -H 'Connection: X-Custom-Hop, X-Other-Hop' \ 378 -H 'X-Custom-Hop: must-not-forward' \ 379 -H 'X-Other-Hop: neither' \ 380 -H 'X-Keep: keep-this' \ 381 -o "$TMPDIR/body" "$(PAIVANA_URL /echo-headers)" 2>"$TMPDIR/err" \ 382 || fail "curl: $(cat "$TMPDIR/err")" 383 if grep -qi '^x-custom-hop:' "$TMPDIR/body"; 384 then 385 fail "X-Custom-Hop leaked to upstream (Connection list ignored)" 386 fi 387 if grep -qi '^x-other-hop:' "$TMPDIR/body"; 388 then 389 fail "X-Other-Hop leaked to upstream (Connection list ignored)" 390 fi 391 grep -qi '^x-keep:.*keep-this' "$TMPDIR/body" || \ 392 fail "X-Keep (not named in Connection) was incorrectly dropped" 393 ok 394 395 # Custom request header must be forwarded. 396 msg "[$label] custom request header X-Test is forwarded" 397 curl -sS -H 'X-Test: dingbat-42' \ 398 -o "$TMPDIR/body" "$(PAIVANA_URL /echo-headers)" 2>"$TMPDIR/err" \ 399 || fail "curl: $(cat "$TMPDIR/err")" 400 grep -qi '^x-test:.*dingbat-42' "$TMPDIR/body" || \ 401 fail "upstream did not see X-Test: dingbat-42" 402 ok 403 404 # Response header passthrough: upstream sets X-Upstream. 405 msg "[$label] upstream response header X-Upstream is forwarded" 406 curl -sS -D "$TMPDIR/hdrs" -o /dev/null "$(PAIVANA_URL /hello)" 2>"$TMPDIR/err" \ 407 || fail "curl: $(cat "$TMPDIR/err")" 408 grep -qi '^x-upstream:' "$TMPDIR/hdrs" || \ 409 fail "X-Upstream header not forwarded back to client" 410 ok 411 } 412 413 ###################################################################### 414 # Cross-cutting tests (do not depend on which upstream is used). 415 ###################################################################### 416 417 function test_method_not_allowed() { 418 msg "unsupported HTTP method (TRACE) yields 405" 419 local status 420 status="$(curl -sS -X TRACE -o /dev/null -w '%{http_code}' \ 421 "$(PAIVANA_URL /hello)" 2>"$TMPDIR/err")" \ 422 || fail "curl: $(cat "$TMPDIR/err")" 423 [ "$status" = "405" ] || fail "status=$status want=405" 424 ok 425 } 426 427 function test_upload_too_big() { 428 msg "upload exceeding 1 MiB buffer yields 413" 429 dd if=/dev/zero of="$TMPDIR/big" bs=1024 count=2048 status=none 430 local status 431 status="$(curl -sS -X POST --data-binary "@$TMPDIR/big" \ 432 -o /dev/null -w '%{http_code}' \ 433 "$(PAIVANA_URL /upload)" 2>"$TMPDIR/err")" \ 434 || fail "curl: $(cat "$TMPDIR/err")" 435 # 413 == Content Too Large; some builds report 500 on hook close 436 [ "$status" = "413" ] || fail "status=$status want=413" 437 ok 438 } 439 440 function test_upload_too_big_early() { 441 # Open a raw TCP connection and send a POST whose Content-Length 442 # already exceeds the buffer cap, but DON'T send any body bytes. 443 # Paivana must reject on the Content-Length header alone (during 444 # MHD's HEADERS_PROCESSED callback), respond with 413 and close 445 # the connection. If the early-reject path is missing the server 446 # would block waiting for a body that never arrives and we'd hit 447 # the timeout, which fails the test rather than masquerading as 448 # a pass. 449 msg "Content-Length exceeding 1 MiB triggers early 413 (no body sent)" 450 local out 451 out="$( ( exec 3<>"/dev/tcp/127.0.0.1/$PAIVANA_PORT" 452 printf 'POST /upload HTTP/1.1\r\nHost: 127.0.0.1:%s\r\nContent-Length: 10485760\r\nConnection: close\r\n\r\n' \ 453 "$PAIVANA_PORT" >&3 454 timeout 5 cat <&3 ) 2>"$TMPDIR/err")" \ 455 || fail "raw POST failed (timeout or socket error): $(cat "$TMPDIR/err")" 456 case "$out" in 457 'HTTP/1.1 413'*) ;; 458 *) fail "expected 413 status line, got: $(echo "$out" | head -c 80)";; 459 esac 460 ok 461 } 462 463 function test_upload_too_big_no_continue() { 464 # When the client opts in to 100-continue, paivana must NOT send 465 # the interim 100 response if it has already decided to reject 466 # the upload — it should jump straight to 413. Use curl with 467 # `Expect: 100-continue` and verbose tracing, then assert that 468 # no `< HTTP/1.1 100` line appeared on the wire. 469 msg "rejection suppresses 100 Continue when client opts in" 470 dd if=/dev/zero of="$TMPDIR/big" bs=1024 count=2048 status=none 471 local status 472 status="$(curl -sSv -X POST -H 'Expect: 100-continue' \ 473 --expect100-timeout 5 \ 474 --data-binary "@$TMPDIR/big" \ 475 -o /dev/null -w '%{http_code}' \ 476 "$(PAIVANA_URL /upload)" 2>"$TMPDIR/trace")" \ 477 || fail "curl: $(cat "$TMPDIR/trace")" 478 [ "$status" = "413" ] || fail "status=$status want=413" 479 if grep -q '^< HTTP/1.1 100' "$TMPDIR/trace"; 480 then 481 fail "server sent 100 Continue before 413; trace: $(grep '^<' "$TMPDIR/trace")" 482 fi 483 ok 484 } 485 486 function test_upload_too_big_chunked() { 487 # Chunked transfer-encoding has no Content-Length, so paivana 488 # cannot know the upload is too big until it actually reaches 489 # the cap mid-stream. This test guards the fallback 490 # drain-then-reject path that runs in BODY_RECEIVING / 491 # FULL_REQ_RECEIVED. 492 msg "chunked upload exceeding 1 MiB still yields 413 (drain path)" 493 dd if=/dev/zero of="$TMPDIR/big" bs=1024 count=2048 status=none 494 local status 495 status="$(curl -sS -X POST \ 496 -H 'Transfer-Encoding: chunked' \ 497 -H 'Content-Length:' \ 498 --data-binary "@$TMPDIR/big" \ 499 -o /dev/null -w '%{http_code}' \ 500 "$(PAIVANA_URL /upload)" 2>"$TMPDIR/err")" \ 501 || fail "curl: $(cat "$TMPDIR/err")" 502 [ "$status" = "413" ] || fail "status=$status want=413" 503 ok 504 } 505 506 function test_upstream_down() { 507 msg "upstream down yields 502 Bad Gateway" 508 # Re-point paivana at a port with nothing listening. 509 stop_paivana 510 start_paivana "http://127.0.0.1:$DEAD_PORT" 511 local status 512 status="$(curl -sS -o "$TMPDIR/body" -w '%{http_code}' \ 513 --max-time 10 \ 514 "$(PAIVANA_URL /hello)" 2>"$TMPDIR/err")" \ 515 || fail "curl: $(cat "$TMPDIR/err")" 516 [ "$status" = "502" ] || fail "status=$status want=502" 517 grep -qi 'bad gateway' "$TMPDIR/body" || \ 518 fail "no 'Bad Gateway' in body" 519 ok 520 } 521 522 # curl with multiple URLs on one command line uses HTTP keep-alive 523 # (not true pipelining, but exercises the same code path in paivana 524 # of handling successive requests on one TCP connection). 525 function test_keepalive_curl() { 526 msg "curl keep-alive: 3 sequential GETs on one connection" 527 local out 528 out="$(curl -sS --http1.1 \ 529 -w '\n@status=%{http_code}\n' \ 530 "$(PAIVANA_URL /hello)" \ 531 "$(PAIVANA_URL /hello)" \ 532 "$(PAIVANA_URL /hello)" 2>"$TMPDIR/err")" \ 533 || fail "curl: $(cat "$TMPDIR/err")" 534 local count 535 count="$(printf '%s\n' "$out" | grep -c '^Hello from')" 536 [ "$count" = "3" ] || fail "got $count Hello lines; want 3; out:\n$out" 537 ok 538 } 539 540 function test_wget_basic() { 541 msg "wget fetch (third-party client interop)" 542 if ! command -v wget >/dev/null 2>&1; then 543 echo "SKIP (wget missing)" 544 return 545 fi 546 local body 547 body="$(wget -qO- --timeout=5 "$(PAIVANA_URL /hello)")" \ 548 || fail "wget failed" 549 echo "$body" | grep -q '^Hello from' \ 550 || fail "unexpected body from wget: $body" 551 ok 552 } 553 554 function test_pipelined() { 555 msg "HTTP/1.1 pipelined requests (4 back-to-back on one TCP socket)" 556 if [ ! -x "$PIPELINE_CLIENT" ]; then 557 echo "SKIP (pipeline_client not built)" 558 return 559 fi 560 local out 561 out="$("$PIPELINE_CLIENT" 127.0.0.1 "$PAIVANA_PORT" \ 562 /hello /status/201 /hello /status/404 2>"$TMPDIR/err")" \ 563 || fail "pipeline_client: $(cat "$TMPDIR/err")" 564 printf '%s\n' "$out" >"$TMPDIR/pipeline.out" 565 local n 566 n="$(grep -c '^--- response' "$TMPDIR/pipeline.out")" 567 [ "$n" = "4" ] || fail "got $n responses, want 4; output:\n$out" 568 # Order preserved: responses must match the request sequence. 569 grep -q '^--- response 0: status=200' "$TMPDIR/pipeline.out" \ 570 || fail "response 0: wrong status; out:\n$out" 571 grep -q '^--- response 1: status=201' "$TMPDIR/pipeline.out" \ 572 || fail "response 1: wrong status; out:\n$out" 573 grep -q '^--- response 2: status=200' "$TMPDIR/pipeline.out" \ 574 || fail "response 2: wrong status; out:\n$out" 575 grep -q '^--- response 3: status=404' "$TMPDIR/pipeline.out" \ 576 || fail "response 3: wrong status; out:\n$out" 577 ok 578 } 579 580 ###################################################################### 581 # Drive the tests. 582 ###################################################################### 583 584 echo "=== paivana reverse-proxy tests ===" 585 echo "Temp dir: $TMPDIR" 586 echo "Paivana binary: $PAIVANA_HTTPD" 587 echo "Source dir: $SRCDIR" 588 echo "Build dir: $BUILDDIR" 589 590 start_upstreams 591 592 # --- C / libmicrohttpd upstream --------------------------------------- 593 start_paivana "http://127.0.0.1:$MHD_PORT" 594 run_battery "mhd" 595 596 test_method_not_allowed 597 test_upload_too_big 598 test_upload_too_big_early 599 test_upload_too_big_no_continue 600 test_upload_too_big_chunked 601 test_keepalive_curl 602 test_wget_basic 603 test_pipelined 604 605 stop_paivana 606 607 # --- Go upstream ------------------------------------------------------ 608 if [ -n "$GO_PORT" ]; 609 then 610 start_paivana "http://127.0.0.1:$GO_PORT" 611 run_battery "go" 612 test_pipelined 613 stop_paivana 614 fi 615 616 # --- Python upstream -------------------------------------------------- 617 if [ -n "$PY_PORT" ]; 618 then 619 start_paivana "http://127.0.0.1:$PY_PORT" 620 run_battery "py" 621 test_pipelined 622 stop_paivana 623 fi 624 625 # --- Rust upstream ---------------------------------------------------- 626 if [ -n "$RS_PORT" ]; 627 then 628 start_paivana "http://127.0.0.1:$RS_PORT" 629 run_battery "rs" 630 test_pipelined 631 stop_paivana 632 fi 633 634 # --- Upstream-down test (runs last because it restarts paivana) ------- 635 test_upstream_down 636 stop_paivana 637 638 echo "=== all tests passed ===" 639 exit 0