stream_upstream.c (35689B)
1 /* 2 This file is part of paivana tests. 3 Copyright (C) 2026 Taler Systems SA 4 5 Paivana is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Affero General Public License 7 as published by the Free Software Foundation; either version 8 3, or (at your option) any later version. 9 10 Paivana is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty 12 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 13 the GNU Affero General Public License for more details. 14 15 You should have received a copy of the GNU Affero General Public 16 License along with Paivana; see the file COPYING. If not, 17 write to the Free Software Foundation, Inc., 51 Franklin 18 Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 */ 20 21 /** 22 * @file stream_upstream.c 23 * @brief Raw-socket HTTP upstream for the streaming tests: serves 24 * bodies far larger than memory, at a controllable rate, and 25 * in the framings a conforming server library will not 26 * produce. 27 * 28 * Raw sockets rather than libmicrohttpd because half of these 29 * targets are deliberate framing abuse -- a declared 30 * `Content-Length` that is not delivered, a chunked response 31 * with no terminating chunk -- which is exactly what a correct 32 * server refuses to emit. 33 * 34 * Bodies are a deterministic function of the byte offset (see 35 * `pattern_at()'), so hundreds of megabytes can be verified 36 * without either side storing them. The same function is in 37 * `stream_client.c'; the two must agree. 38 * 39 * Targets, selected by request path: 40 * 41 * /cl?bytes=N[&rate=R] declare Content-Length: N, send N 42 * /chunked?bytes=N[&rate=R] same body, chunked, no length 43 * /short?declare=N&send=M declare N, send M < N, close 44 * /chunk-abort?after=N chunked, N bytes, no terminator 45 * /hang?after=N N bytes, then nothing, never close 46 * /slowstart?delay=D[&bytes=N] headers now, first byte in D ms 47 * /sink[?rate=R] read a request body, report its size 48 * /sink-early?after=N answer 413 after N bytes of body 49 * /status?code=C[&len=N] bare status, optional Content-Length 50 * /range?bytes=N honour one Range, answer 206 51 * /mute accept, then never answer at all 52 * /trailers?bytes=N chunked, with a trailer section 53 * /interim?bytes=N a 103 before the final response 54 * 55 * `rate' is in bytes per second; 0 (the default) means as fast 56 * as the socket takes it. A rate limit is what makes the 57 * congestion cases reproducible: without it the kernel buffers 58 * absorb everything and no backpressure is ever exercised. 59 */ 60 #ifndef _GNU_SOURCE 61 #define _GNU_SOURCE 62 #endif 63 #include "platform.h" 64 #include <gnunet/gnunet_util_lib.h> 65 #include <limits.h> 66 #include <poll.h> 67 #include <strings.h> 68 #include <time.h> 69 70 /** 71 * Bytes written per iteration of a rate-limited send loop. Small 72 * enough that a slow rate really does dribble rather than arriving in 73 * one burst per second, large enough not to make a 200 MiB unlimited 74 * transfer syscall-bound. 75 */ 76 #define SEND_GRAIN 16384 77 78 /** 79 * How long (ms) `/hang' parks a connection before giving up on the 80 * peer. Only reached when paivana's own stall watchdog failed to 81 * fire, which is the bug the case is looking for; the bound is here 82 * so a wedged run still terminates. 83 */ 84 #define HANG_TIMEOUT_MS 120000 85 86 /** 87 * Size of the request header block we are willing to read. 88 */ 89 #define HDR_MAX 16384 90 91 92 static volatile sig_atomic_t run_flag = 1; 93 94 /** 95 * Body bytes this connection moved, in whichever direction it was 96 * moving them. Reported when the connection is done; see `serve()'. 97 */ 98 static uint64_t req_bytes; 99 100 /** 101 * What the connection was asked for, for the report line. Sized to 102 * hold a whole request target, so that a long query string is reported 103 * rather than silently cut. 104 */ 105 static char req_label[1024]; 106 107 108 static void 109 on_sig (int sig) 110 { 111 (void) sig; 112 run_flag = 0; 113 } 114 115 116 /** 117 * The body byte at offset @a off. 118 * 119 * Deterministic and cheap, and deliberately not a constant: a body of 120 * one repeated character would pass a comparison that dropped or 121 * duplicated a whole aligned block, which is precisely the mistake a 122 * ring buffer with wrong wrap arithmetic makes. The `off >> 13' term 123 * makes the pattern differ between 8 KiB blocks as well as within 124 * them. 125 * 126 * Must agree with the same function in `stream_client.c'. 127 * 128 * @param off byte offset into the body 129 * @return the byte that belongs there 130 */ 131 static uint8_t 132 pattern_at (uint64_t off) 133 { 134 return (uint8_t) ((off * 7) + (off >> 13)); 135 } 136 137 138 /** 139 * Write all of @a len bytes from @a buf to @a fd. 140 * 141 * @param fd socket to write to 142 * @param buf bytes to write 143 * @param len number of bytes in @a buf 144 * @return true if all of it went out 145 */ 146 static bool 147 write_all (int fd, 148 const void *buf, 149 size_t len) 150 { 151 const char *p = buf; 152 size_t off = 0; 153 154 while (off < len) 155 { 156 ssize_t n = write (fd, 157 p + off, 158 len - off); 159 160 if (n < 0) 161 { 162 if (EINTR == errno) 163 continue; 164 return false; 165 } 166 if (0 == n) 167 return false; 168 off += (size_t) n; 169 } 170 return true; 171 } 172 173 174 /** 175 * Sleep for @a ms milliseconds, resuming across signals. 176 */ 177 static void 178 sleep_ms (unsigned int ms) 179 { 180 struct timespec ts = { 181 .tv_sec = ms / 1000, 182 .tv_nsec = (long) (ms % 1000) * 1000000L 183 }; 184 185 while ( (0 != nanosleep (&ts, &ts)) && 186 (EINTR == errno) ) 187 ; /* again */ 188 } 189 190 191 /** 192 * Send @a total bytes of the pattern starting at offset @a start, 193 * at most @a rate bytes per second. 194 * 195 * The rate is enforced by sleeping between grains rather than by 196 * pacing against a clock, which would let a stalled peer be 197 * compensated for with a burst -- and a burst is exactly what these 198 * tests must not see. 199 * 200 * @param fd socket to write to 201 * @param start first body offset to send 202 * @param total number of bytes to send 203 * @param rate bytes per second, 0 for unlimited 204 * @return true if all of it went out 205 */ 206 static bool 207 send_pattern (int fd, 208 uint64_t start, 209 uint64_t total, 210 unsigned long long rate) 211 { 212 static char grain[SEND_GRAIN]; 213 uint64_t sent = 0; 214 size_t step = SEND_GRAIN; 215 unsigned int nap = 0; 216 217 if (0 != rate) 218 { 219 /* Aim for roughly ten writes a second, but never below one byte 220 per write and never above a grain. */ 221 step = (size_t) GNUNET_MIN ((unsigned long long) SEND_GRAIN, 222 GNUNET_MAX (1ULL, rate / 10)); 223 nap = (unsigned int) ((1000ULL * step) / rate); 224 } 225 while (sent < total) 226 { 227 size_t n = (size_t) GNUNET_MIN ((uint64_t) step, 228 total - sent); 229 230 for (size_t i = 0; i < n; i++) 231 grain[i] = (char) pattern_at (start + sent + i); 232 if (! write_all (fd, 233 grain, 234 n)) 235 return false; 236 sent += n; 237 if ( (0 != nap) && 238 (sent < total) ) 239 sleep_ms (nap); 240 if (! run_flag) 241 return false; 242 } 243 return true; 244 } 245 246 247 /** 248 * Like `send_pattern()', but reports how much actually went out rather 249 * than only whether all of it did. 250 * 251 * The count is the backpressure evidence. A proxy that assembled the 252 * body in memory would take everything at line rate however slowly its 253 * own client was reading; one that relays it can only take what the 254 * client has made room for. So how far this gets, and how long it 255 * takes to get there, is what says which of the two is happening -- 256 * and it cannot be seen from the client end, where both look the same. 257 * 258 * @param fd socket to write to 259 * @param start first body offset to send 260 * @param total number of bytes to send 261 * @param rate bytes per second, 0 for unlimited 262 * @return number of bytes written 263 */ 264 static uint64_t 265 send_pattern_counted (int fd, 266 uint64_t start, 267 uint64_t total, 268 unsigned long long rate) 269 { 270 static char grain[SEND_GRAIN]; 271 uint64_t sent = 0; 272 size_t step = SEND_GRAIN; 273 unsigned int nap = 0; 274 275 if (0 != rate) 276 { 277 step = (size_t) GNUNET_MIN ((unsigned long long) SEND_GRAIN, 278 GNUNET_MAX (1ULL, rate / 10)); 279 nap = (unsigned int) ((1000ULL * step) / rate); 280 } 281 while (sent < total) 282 { 283 size_t n = (size_t) GNUNET_MIN ((uint64_t) step, 284 total - sent); 285 286 for (size_t i = 0; i < n; i++) 287 grain[i] = (char) pattern_at (start + sent + i); 288 if (! write_all (fd, 289 grain, 290 n)) 291 return sent; 292 sent += n; 293 if ( (0 != nap) && 294 (sent < total) ) 295 sleep_ms (nap); 296 if (! run_flag) 297 return sent; 298 } 299 return sent; 300 } 301 302 303 /** 304 * Body bytes the last `send_chunked()' managed to write, which is not 305 * the same as what it was asked for when the peer stops reading. 306 */ 307 static uint64_t chunked_sent; 308 309 310 /** 311 * Send @a total bytes of the pattern as HTTP chunks. 312 * 313 * @param fd socket to write to 314 * @param total number of body bytes 315 * @param rate bytes per second, 0 for unlimited 316 * @param terminate write the terminating zero-length chunk 317 * @return true if all of it went out 318 */ 319 static bool 320 send_chunked (int fd, 321 uint64_t total, 322 unsigned long long rate, 323 bool terminate) 324 { 325 static char grain[SEND_GRAIN]; 326 uint64_t sent = 0; 327 size_t step = SEND_GRAIN; 328 unsigned int nap = 0; 329 char hdr[32]; 330 331 chunked_sent = 0; 332 333 if (0 != rate) 334 { 335 step = (size_t) GNUNET_MIN ((unsigned long long) SEND_GRAIN, 336 GNUNET_MAX (1ULL, rate / 10)); 337 nap = (unsigned int) ((1000ULL * step) / rate); 338 } 339 while (sent < total) 340 { 341 size_t n = (size_t) GNUNET_MIN ((uint64_t) step, 342 total - sent); 343 int hl = snprintf (hdr, 344 sizeof (hdr), 345 "%zx\r\n", 346 n); 347 348 for (size_t i = 0; i < n; i++) 349 grain[i] = (char) pattern_at (sent + i); 350 if ( (! write_all (fd, hdr, (size_t) hl)) || 351 (! write_all (fd, grain, n)) || 352 (! write_all (fd, "\r\n", 2)) ) 353 return false; 354 sent += n; 355 chunked_sent = sent; 356 if ( (0 != nap) && 357 (sent < total) ) 358 sleep_ms (nap); 359 if (! run_flag) 360 return false; 361 } 362 if (terminate) 363 return write_all (fd, 364 "0\r\n\r\n", 365 5); 366 return true; 367 } 368 369 370 /** 371 * Read from @a fd into @a buf until "\r\n\r\n" appears. 372 * 373 * @param fd socket to read from 374 * @param[out] buf where to put the header block 375 * @param cap capacity of @a buf 376 * @param[out] eoh set to the offset just past the CRLFCRLF 377 * @return total bytes read, or -1 on error or overlong headers 378 */ 379 static ssize_t 380 read_until_eoh (int fd, 381 char *buf, 382 size_t cap, 383 size_t *eoh) 384 { 385 size_t pos = 0; 386 size_t scan = 0; 387 388 while (pos < cap) 389 { 390 ssize_t n = read (fd, 391 buf + pos, 392 cap - pos); 393 394 if (n <= 0) 395 return -1; 396 pos += (size_t) n; 397 while (scan + 3 < pos) 398 { 399 if ( ('\r' == buf[scan]) && 400 ('\n' == buf[scan + 1]) && 401 ('\r' == buf[scan + 2]) && 402 ('\n' == buf[scan + 3]) ) 403 { 404 *eoh = scan + 4; 405 return (ssize_t) pos; 406 } 407 scan++; 408 } 409 } 410 return -1; 411 } 412 413 414 /** 415 * Value of the query parameter @a name in the request target @a 416 * target, or @a dflt if it is absent or unparseable. 417 * 418 * @param target request target, NUL-terminated 419 * @param name parameter name 420 * @param dflt value to use if it is not there 421 * @return the parsed value 422 */ 423 static unsigned long long 424 query_num (const char *target, 425 const char *name, 426 unsigned long long dflt) 427 { 428 const char *q = strchr (target, '?'); 429 size_t nlen = strlen (name); 430 431 if (NULL == q) 432 return dflt; 433 for (const char *p = q + 1; '\0' != *p;) 434 { 435 if ( (0 == strncmp (p, name, nlen)) && 436 ('=' == p[nlen]) ) 437 { 438 char *end; 439 unsigned long long v; 440 441 errno = 0; 442 v = strtoull (&p[nlen + 1], 443 &end, 444 10); 445 if ( (0 != errno) || 446 (end == &p[nlen + 1]) ) 447 return dflt; 448 return v; 449 } 450 p = strchr (p, '&'); 451 if (NULL == p) 452 break; 453 p++; 454 } 455 return dflt; 456 } 457 458 459 /** 460 * Does @a target name the path @a path (ignoring any query)? 461 */ 462 static bool 463 path_is (const char *target, 464 const char *path) 465 { 466 size_t plen = strlen (path); 467 468 return (0 == strncmp (target, path, plen)) && 469 ( ('\0' == target[plen]) || 470 ('?' == target[plen]) ); 471 } 472 473 474 /** 475 * Find the Content-Length in the request header block. 476 * 477 * @param hdr start of the (not NUL-terminated) header block 478 * @param len number of bytes in @a hdr 479 * @return the announced length, or -1 if there was none 480 */ 481 static long long 482 find_content_length (const char *hdr, 483 size_t len) 484 { 485 static const char name[] = "content-length:"; 486 const size_t nlen = sizeof (name) - 1; 487 488 for (size_t i = 0; i + nlen <= len; i++) 489 { 490 size_t j; 491 long long v = 0; 492 bool digits = false; 493 494 if ( (0 == i) || 495 ('\n' != hdr[i - 1]) ) 496 continue; 497 if (0 != strncasecmp (&hdr[i], 498 name, 499 nlen)) 500 continue; 501 j = i + nlen; 502 while ( (j < len) && 503 ( (' ' == hdr[j]) || ('\t' == hdr[j]) ) ) 504 j++; 505 while ( (j < len) && 506 ('0' <= hdr[j]) && 507 ('9' >= hdr[j]) ) 508 { 509 if (v > (LLONG_MAX - (hdr[j] - '0')) / 10) 510 return -1; 511 v = v * 10 + (hdr[j] - '0'); 512 digits = true; 513 j++; 514 } 515 return digits ? v : -1; 516 } 517 return -1; 518 } 519 520 521 /** 522 * Is the request header block chunked? 523 * 524 * @param hdr start of the header block 525 * @param len number of bytes in @a hdr 526 * @return true if a Transfer-Encoding naming chunked is present 527 */ 528 static bool 529 is_chunked_request (const char *hdr, 530 size_t len) 531 { 532 static const char name[] = "transfer-encoding:"; 533 const size_t nlen = sizeof (name) - 1; 534 535 for (size_t i = 0; i + nlen <= len; i++) 536 { 537 if ( (0 == i) || 538 ('\n' != hdr[i - 1]) ) 539 continue; 540 if (0 == strncasecmp (&hdr[i], 541 name, 542 nlen)) 543 return true; 544 } 545 return false; 546 } 547 548 549 /** 550 * Read and verify a request body, at most @a rate bytes per second. 551 * 552 * Verification is against `pattern_at()', so a body that arrived 553 * complete but reordered or with a duplicated block is caught -- the 554 * byte count alone would not notice. Both framings are handled: a 555 * declared length, and chunked, which is what libcurl sends when the 556 * length is not known in advance. 557 * 558 * @param fd socket to read from 559 * @param pre bytes of body already read with the headers 560 * @param pre_len number of bytes in @a pre 561 * @param declared Content-Length, or -1 for chunked/unknown 562 * @param chunked read the body as chunks 563 * @param rate bytes per second, 0 for unlimited 564 * @param stop_after stop reading after this many body bytes, 0 for 565 * "read it all" 566 * @param[out] ok set to false if the pattern did not match 567 * @return number of body bytes read 568 */ 569 static uint64_t 570 drain_body (int fd, 571 const char *pre, 572 size_t pre_len, 573 long long declared, 574 bool chunked, 575 unsigned long long rate, 576 uint64_t stop_after, 577 bool *ok) 578 { 579 static char buf[SEND_GRAIN]; 580 uint64_t got = 0; 581 size_t step = sizeof (buf); 582 unsigned int nap = 0; 583 /* Chunk framing is stripped by a tiny state machine rather than a 584 parser: we only ever read what libcurl writes. The state has to 585 survive a read() boundary falling anywhere -- including between a 586 chunk's last data byte and its trailing CRLF, which is why 587 skipping that CRLF is a state and not two bytes consumed on the 588 spot. Getting that wrong makes the next read start on "\r\n", 589 which parses as an empty chunk-size line, which reads as the 590 terminating chunk: the body ends early and the byte count is 591 short but self-consistent. */ 592 enum 593 { 594 CH_HDR, /* accumulating the chunk-size line */ 595 CH_DATA, /* @e chunk_left bytes of chunk data to come */ 596 CH_CRLF /* @e crlf_left bytes of the post-data CRLF to skip */ 597 } cstate = CH_HDR; 598 uint64_t chunk_left = 0; 599 unsigned int crlf_left = 0; 600 char hdrline[32]; 601 size_t hdrpos = 0; 602 603 *ok = true; 604 if (0 != rate) 605 { 606 step = (size_t) GNUNET_MIN ((unsigned long long) sizeof (buf), 607 GNUNET_MAX (1ULL, rate / 10)); 608 nap = (unsigned int) ((1000ULL * step) / rate); 609 } 610 while (run_flag) 611 { 612 const char *p; 613 size_t avail; 614 ssize_t n; 615 616 if (0 != pre_len) 617 { 618 p = pre; 619 avail = pre_len; 620 pre_len = 0; 621 } 622 else 623 { 624 if ( (! chunked) && 625 (declared >= 0) && 626 (got >= (uint64_t) declared) ) 627 break; 628 if ( (0 != stop_after) && 629 (got >= stop_after) ) 630 break; 631 n = read (fd, 632 buf, 633 step); 634 if (n <= 0) 635 break; 636 p = buf; 637 avail = (size_t) n; 638 if (0 != nap) 639 sleep_ms (nap); 640 } 641 while (0 != avail) 642 { 643 if (! chunked) 644 { 645 for (size_t i = 0; i < avail; i++) 646 if ((uint8_t) p[i] != pattern_at (got + i)) 647 { 648 *ok = false; 649 break; 650 } 651 got += avail; 652 avail = 0; 653 continue; 654 } 655 switch (cstate) 656 { 657 case CH_HDR: 658 /* Accumulate up to the CRLF that ends the chunk size line. */ 659 while ( (0 != avail) && 660 (hdrpos + 1 < sizeof (hdrline)) ) 661 { 662 char c = *p++; 663 664 avail--; 665 hdrline[hdrpos++] = c; 666 if ( (hdrpos >= 2) && 667 ('\r' == hdrline[hdrpos - 2]) && 668 ('\n' == hdrline[hdrpos - 1]) ) 669 { 670 hdrline[hdrpos - 2] = '\0'; 671 chunk_left = strtoull (hdrline, 672 NULL, 673 16); 674 hdrpos = 0; 675 cstate = (0 == chunk_left) ? CH_HDR : CH_DATA; 676 break; 677 } 678 } 679 if ( (CH_HDR == cstate) && 680 (0 == hdrpos) ) 681 return got; /* terminating chunk */ 682 break; 683 case CH_DATA: 684 { 685 size_t take = (size_t) GNUNET_MIN ((uint64_t) avail, 686 chunk_left); 687 688 for (size_t i = 0; i < take; i++) 689 if ((uint8_t) p[i] != pattern_at (got + i)) 690 { 691 *ok = false; 692 break; 693 } 694 got += take; 695 p += take; 696 avail -= take; 697 chunk_left -= take; 698 if (0 == chunk_left) 699 { 700 cstate = CH_CRLF; 701 crlf_left = 2; 702 } 703 } 704 break; 705 case CH_CRLF: 706 { 707 size_t skip = (size_t) GNUNET_MIN ((uint64_t) avail, 708 (uint64_t) crlf_left); 709 710 p += skip; 711 avail -= skip; 712 crlf_left -= (unsigned int) skip; 713 if (0 == crlf_left) 714 { 715 cstate = CH_HDR; 716 hdrpos = 0; 717 } 718 } 719 break; 720 } 721 } 722 } 723 return got; 724 } 725 726 727 /** 728 * Serve one connection. 729 * 730 * @param fd the accepted socket 731 */ 732 static void 733 serve (int fd) 734 { 735 char hdr[HDR_MAX]; 736 char target[1024]; 737 size_t eoh = 0; 738 ssize_t total; 739 char resp[512]; 740 int rl; 741 unsigned long long rate; 742 743 total = read_until_eoh (fd, 744 hdr, 745 sizeof (hdr), 746 &eoh); 747 if (0 > total) 748 return; 749 { 750 /* Request line: METHOD SP target SP version */ 751 const char *sp1 = memchr (hdr, ' ', (size_t) total); 752 const char *sp2; 753 size_t tlen; 754 755 if (NULL == sp1) 756 return; 757 sp2 = memchr (sp1 + 1, 758 ' ', 759 (size_t) total - (size_t) (sp1 + 1 - hdr)); 760 if (NULL == sp2) 761 return; 762 tlen = (size_t) (sp2 - sp1 - 1); 763 if (tlen >= sizeof (target)) 764 return; 765 memcpy (target, 766 sp1 + 1, 767 tlen); 768 target[tlen] = '\0'; 769 } 770 rate = query_num (target, 771 "rate", 772 0); 773 snprintf (req_label, 774 sizeof (req_label), 775 "%s", 776 target); 777 778 if (path_is (target, "/cl")) 779 { 780 uint64_t bytes = query_num (target, 781 "bytes", 782 1024); 783 784 rl = snprintf (resp, 785 sizeof (resp), 786 "HTTP/1.1 200 OK\r\n" 787 "Content-Type: application/octet-stream\r\n" 788 "Content-Length: %llu\r\n" 789 "\r\n", 790 (unsigned long long) bytes); 791 if (write_all (fd, resp, (size_t) rl)) 792 req_bytes = send_pattern_counted (fd, 793 0, 794 bytes, 795 rate); 796 return; 797 } 798 if (path_is (target, "/chunked")) 799 { 800 uint64_t bytes = query_num (target, 801 "bytes", 802 1024); 803 804 rl = snprintf (resp, 805 sizeof (resp), 806 "HTTP/1.1 200 OK\r\n" 807 "Content-Type: application/octet-stream\r\n" 808 "Transfer-Encoding: chunked\r\n" 809 "\r\n"); 810 if (write_all (fd, resp, (size_t) rl)) 811 (void) send_chunked (fd, 812 bytes, 813 rate, 814 true); 815 req_bytes = chunked_sent; 816 return; 817 } 818 if (path_is (target, "/short")) 819 { 820 uint64_t declare = query_num (target, 821 "declare", 822 100000); 823 uint64_t send = query_num (target, 824 "send", 825 1000); 826 827 rl = snprintf (resp, 828 sizeof (resp), 829 "HTTP/1.1 200 OK\r\n" 830 "Content-Type: application/octet-stream\r\n" 831 "Content-Length: %llu\r\n" 832 "\r\n", 833 (unsigned long long) declare); 834 if (write_all (fd, resp, (size_t) rl)) 835 (void) send_pattern (fd, 836 0, 837 send, 838 rate); 839 return; 840 } 841 if (path_is (target, "/chunk-abort")) 842 { 843 uint64_t after = query_num (target, 844 "after", 845 1000); 846 847 rl = snprintf (resp, 848 sizeof (resp), 849 "HTTP/1.1 200 OK\r\n" 850 "Content-Type: application/octet-stream\r\n" 851 "Transfer-Encoding: chunked\r\n" 852 "\r\n"); 853 if (write_all (fd, resp, (size_t) rl)) 854 (void) send_chunked (fd, 855 after, 856 rate, 857 false); /* no terminating chunk */ 858 return; 859 } 860 if (path_is (target, "/hang")) 861 { 862 uint64_t after = query_num (target, 863 "after", 864 1000); 865 struct pollfd pfd = { 866 .fd = fd, 867 .events = POLLRDHUP 868 }; 869 870 rl = snprintf (resp, 871 sizeof (resp), 872 "HTTP/1.1 200 OK\r\n" 873 "Content-Type: application/octet-stream\r\n" 874 "Transfer-Encoding: chunked\r\n" 875 "\r\n"); 876 if (! write_all (fd, resp, (size_t) rl)) 877 return; 878 if (! send_chunked (fd, 879 after, 880 rate, 881 false)) 882 return; 883 /* Now say nothing, and do not close: the point is a connection 884 that is open, established and idle, which only paivana's own 885 stall watchdog can resolve. */ 886 (void) poll (&pfd, 887 1, 888 HANG_TIMEOUT_MS); 889 return; 890 } 891 if (path_is (target, "/slowstart")) 892 { 893 uint64_t delay = query_num (target, 894 "delay", 895 1000); 896 uint64_t bytes = query_num (target, 897 "bytes", 898 1024); 899 900 rl = snprintf (resp, 901 sizeof (resp), 902 "HTTP/1.1 200 OK\r\n" 903 "Content-Type: application/octet-stream\r\n" 904 "Content-Length: %llu\r\n" 905 "\r\n", 906 (unsigned long long) bytes); 907 if (! write_all (fd, resp, (size_t) rl)) 908 return; 909 sleep_ms ((unsigned int) delay); 910 (void) send_pattern (fd, 911 0, 912 bytes, 913 rate); 914 return; 915 } 916 if (path_is (target, "/sink")) 917 { 918 long long declared = find_content_length (hdr, 919 eoh); 920 bool chunked = is_chunked_request (hdr, 921 eoh); 922 bool ok; 923 uint64_t got; 924 char body[128]; 925 int bl; 926 927 got = drain_body (fd, 928 &hdr[eoh], 929 (size_t) total - eoh, 930 declared, 931 chunked, 932 rate, 933 0, 934 &ok); 935 req_bytes = got; 936 bl = snprintf (body, 937 sizeof (body), 938 "bytes=%llu framing=%s pattern=%s\n", 939 (unsigned long long) got, 940 chunked ? "chunked" : "length", 941 ok ? "ok" : "CORRUPT"); 942 rl = snprintf (resp, 943 sizeof (resp), 944 "HTTP/1.1 200 OK\r\n" 945 "Content-Type: text/plain\r\n" 946 "Content-Length: %d\r\n" 947 "\r\n", 948 bl); 949 if (write_all (fd, resp, (size_t) rl)) 950 (void) write_all (fd, body, (size_t) bl); 951 return; 952 } 953 if (path_is (target, "/sink-early")) 954 { 955 uint64_t after = query_num (target, 956 "after", 957 1024); 958 long long declared = find_content_length (hdr, 959 eoh); 960 bool chunked = is_chunked_request (hdr, 961 eoh); 962 bool ok; 963 static const char body[] = "too large\n"; 964 965 (void) drain_body (fd, 966 &hdr[eoh], 967 (size_t) total - eoh, 968 declared, 969 chunked, 970 rate, 971 after, 972 &ok); 973 /* Answer while the body is (very probably) still coming. This is 974 the case that only exists because the request is streamed: with 975 the body buffered first, the origin could not have answered 976 before seeing all of it. */ 977 rl = snprintf (resp, 978 sizeof (resp), 979 "HTTP/1.1 413 Content Too Large\r\n" 980 "Content-Type: text/plain\r\n" 981 "Connection: close\r\n" 982 "Content-Length: %zu\r\n" 983 "\r\n%s", 984 sizeof (body) - 1, 985 body); 986 (void) write_all (fd, resp, (size_t) rl); 987 return; 988 } 989 if (path_is (target, "/range")) 990 { 991 /* Honour a single "bytes=A-B" range, so that the 206 and its 992 Content-Range can be checked end to end. Nothing here parses 993 the general grammar: the driver sends one form. */ 994 uint64_t bytes = query_num (target, 995 "bytes", 996 1048576); 997 unsigned long long from = 0; 998 unsigned long long to = bytes - 1; 999 const char *r = NULL; 1000 1001 for (size_t i = 0; i + 6 <= eoh; i++) 1002 if ( (0 != i) && 1003 ('\n' == hdr[i - 1]) && 1004 (0 == strncasecmp (&hdr[i], "range:", 6)) ) 1005 { 1006 r = &hdr[i + 6]; 1007 break; 1008 } 1009 if (NULL != r) 1010 { 1011 const char *eq = strchr (r, '='); 1012 1013 if (NULL != eq) 1014 { 1015 char *end; 1016 1017 from = strtoull (eq + 1, &end, 10); 1018 if ('-' == *end) 1019 { 1020 unsigned long long t = strtoull (end + 1, &end, 10); 1021 1022 if (0 != t) 1023 to = t; 1024 } 1025 } 1026 } 1027 if (to >= bytes) 1028 to = bytes - 1; 1029 rl = snprintf (resp, 1030 sizeof (resp), 1031 "HTTP/1.1 206 Partial Content\r\n" 1032 "Content-Type: application/octet-stream\r\n" 1033 "Content-Range: bytes %llu-%llu/%llu\r\n" 1034 "Content-Length: %llu\r\n" 1035 "\r\n", 1036 from, 1037 to, 1038 (unsigned long long) bytes, 1039 to - from + 1); 1040 if (write_all (fd, resp, (size_t) rl)) 1041 (void) send_pattern (fd, 1042 from, 1043 to - from + 1, 1044 rate); 1045 return; 1046 } 1047 if (path_is (target, "/mute")) 1048 { 1049 /* Accept the connection and say nothing at all. Distinct from an 1050 origin that is not there, which is a 502: this one is a 504, and 1051 telling them apart is what paivana's time-to-headers clock is 1052 for. */ 1053 struct pollfd pfd = { 1054 .fd = fd, 1055 .events = POLLRDHUP 1056 }; 1057 1058 (void) poll (&pfd, 1059 1, 1060 HANG_TIMEOUT_MS); 1061 return; 1062 } 1063 if (path_is (target, "/trailers")) 1064 { 1065 /* A chunked response with a trailer section. RFC 9110 ยง6.5.1 1066 forbids merging a trailer into the header section, and the 1067 response has in any case already been queued by the time these 1068 arrive, so they must be dropped rather than added to it. */ 1069 uint64_t bytes = query_num (target, 1070 "bytes", 1071 4096); 1072 1073 rl = snprintf (resp, 1074 sizeof (resp), 1075 "HTTP/1.1 200 OK\r\n" 1076 "Content-Type: application/octet-stream\r\n" 1077 "Trailer: X-Trailer-Check\r\n" 1078 "Transfer-Encoding: chunked\r\n" 1079 "\r\n"); 1080 if (! write_all (fd, resp, (size_t) rl)) 1081 return; 1082 if (! send_chunked (fd, 1083 bytes, 1084 rate, 1085 false)) 1086 return; 1087 (void) write_all (fd, 1088 "0\r\n" 1089 "X-Trailer-Check: leaked\r\n" 1090 "\r\n", 1091 strlen ("0\r\nX-Trailer-Check: leaked\r\n\r\n")); 1092 return; 1093 } 1094 if (path_is (target, "/interim")) 1095 { 1096 /* A 1xx before the final response, carrying a header that must not 1097 reappear on it. */ 1098 uint64_t bytes = query_num (target, 1099 "bytes", 1100 4096); 1101 1102 rl = snprintf (resp, 1103 sizeof (resp), 1104 "HTTP/1.1 103 Early Hints\r\n" 1105 "X-Interim-Check: leaked\r\n" 1106 "\r\n" 1107 "HTTP/1.1 200 OK\r\n" 1108 "Content-Type: application/octet-stream\r\n" 1109 "Content-Length: %llu\r\n" 1110 "\r\n", 1111 (unsigned long long) bytes); 1112 if (write_all (fd, resp, (size_t) rl)) 1113 (void) send_pattern (fd, 1114 0, 1115 bytes, 1116 rate); 1117 return; 1118 } 1119 if (path_is (target, "/status")) 1120 { 1121 unsigned long long code = query_num (target, 1122 "code", 1123 204); 1124 unsigned long long len = query_num (target, 1125 "len", 1126 UINT64_MAX); 1127 1128 if (UINT64_MAX == len) 1129 rl = snprintf (resp, 1130 sizeof (resp), 1131 "HTTP/1.1 %llu Status\r\n" 1132 "\r\n", 1133 code); 1134 else 1135 rl = snprintf (resp, 1136 sizeof (resp), 1137 "HTTP/1.1 %llu Status\r\n" 1138 "Content-Length: %llu\r\n" 1139 "\r\n", 1140 code, 1141 len); 1142 (void) write_all (fd, resp, (size_t) rl); 1143 return; 1144 } 1145 rl = snprintf (resp, 1146 sizeof (resp), 1147 "HTTP/1.1 404 Not Found\r\n" 1148 "Content-Length: 0\r\n" 1149 "\r\n"); 1150 (void) write_all (fd, resp, (size_t) rl); 1151 } 1152 1153 1154 int 1155 main (int argc, 1156 char **argv) 1157 { 1158 int lsock; 1159 int port; 1160 int one = 1; 1161 struct sockaddr_in addr; 1162 1163 if (2 > argc) 1164 { 1165 fprintf (stderr, 1166 "usage: %s PORT\n", 1167 argv[0]); 1168 return 1; 1169 } 1170 port = atoi (argv[1]); 1171 signal (SIGINT, &on_sig); 1172 signal (SIGTERM, &on_sig); 1173 /* A peer that goes away mid-body is the normal case here, not an 1174 error; without this the first such write kills the process. */ 1175 signal (SIGPIPE, SIG_IGN); 1176 lsock = socket (AF_INET, 1177 SOCK_STREAM, 1178 0); 1179 if (0 > lsock) 1180 { 1181 perror ("socket"); 1182 return 1; 1183 } 1184 (void) setsockopt (lsock, 1185 SOL_SOCKET, 1186 SO_REUSEADDR, 1187 &one, 1188 sizeof (one)); 1189 memset (&addr, 0, sizeof (addr)); 1190 addr.sin_family = AF_INET; 1191 addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); 1192 addr.sin_port = htons ((uint16_t) port); 1193 if (0 != bind (lsock, 1194 (struct sockaddr *) &addr, 1195 sizeof (addr))) 1196 { 1197 perror ("bind"); 1198 return 1; 1199 } 1200 if (0 != listen (lsock, 64)) 1201 { 1202 perror ("listen"); 1203 return 1; 1204 } 1205 fprintf (stderr, 1206 "stream_upstream listening on port %d\n", 1207 port); 1208 fflush (stderr); 1209 while (run_flag) 1210 { 1211 int fd = accept (lsock, NULL, NULL); 1212 pid_t pid; 1213 1214 if (0 > fd) 1215 { 1216 if (EINTR == errno) 1217 continue; 1218 break; 1219 } 1220 /* One process per connection. Not for throughput but because 1221 several of the targets deliberately do not finish -- `/hang' 1222 parks for two minutes, `/cl?rate=' dribbles for as long as the 1223 driver asks -- and a single-threaded accept loop would make the 1224 concurrency cases serialise behind them. */ 1225 pid = fork (); 1226 if (0 == pid) 1227 { 1228 struct timespec t0; 1229 struct timespec t1; 1230 1231 close (lsock); 1232 clock_gettime (CLOCK_MONOTONIC, 1233 &t0); 1234 serve (fd); 1235 clock_gettime (CLOCK_MONOTONIC, 1236 &t1); 1237 /* One line per connection, for the cases that assert on how the 1238 origin was paced rather than on what the client received. 1239 Written after the socket work so the timing covers it, and to 1240 stderr so it lands in the driver's log unbuffered. */ 1241 fprintf (stderr, 1242 "served target=%s bytes=%llu ms=%ld\n", 1243 req_label, 1244 (unsigned long long) req_bytes, 1245 (long) ((t1.tv_sec - t0.tv_sec) * 1000 1246 + (t1.tv_nsec - t0.tv_nsec) / 1000000)); 1247 fflush (stderr); 1248 close (fd); 1249 _exit (0); 1250 } 1251 close (fd); 1252 if (0 > pid) 1253 perror ("fork"); 1254 /* Reap whatever has finished; no zombies, no blocking. */ 1255 while (0 < waitpid (-1, NULL, WNOHANG)) 1256 ; /* again */ 1257 } 1258 close (lsock); 1259 return 0; 1260 } 1261 1262 1263 /* end of stream_upstream.c */