. "$BST_LIB" # restinio 0.6.13 -- a header-only C++14 HTTP/WebSocket server that installs # completely on this image and CANNOT BE COMPILED AGAINST here. # # That sentence is the whole suite. A file listing cannot reach it, and a suite # written from the package's description would assert 127 headers, three cmake # files and a version string, go green, and say nothing true about what a # consumer gets. So most of what follows is the three things a listing cannot # see: # # THE EXCEPTIONS GATE IS UNGUARDED, AND THAT IS THE PORT'S HEADLINE. This # arm's C++ driver bakes -fno-exceptions into every compile # (BRINTOS_CXX_EH). restinio assumes exceptions unconditionally: 40 of its # shipped headers emit a bare `throw` or open a `try`, and -- unlike pegtl, # which treats the same state as a first-class configuration and guards 30 # headers on __cpp_exceptions -- restinio's own code carries NOT ONE such # guard. Section 3 asserts that by reading the installed headers, because it # is the fact a consumer on this image most needs and nothing in the payload # announces it. # THE UNREACHABLE HEADER IS THE ONE EVERY ENTRY POINT GOES THROUGH. # http_headers.hpp is in the 40, and all.hpp, http_server.hpp, traits.hpp, # settings.hpp, request_handler.hpp and router/express.hpp all include it, # directly or at one remove. So the server half is not awkward here, it is # absent. Section 3 pins the include edges that make it so, from the shipped # files, so that a later restinio release which moved the throw would show up # here rather than in a consumer's build six months on. # THE SHIPPED CMAKE PACKAGE CARRIES TWO CORRECTIONS AND MUST KEEP THEM. See # section 2. Upstream's generated export names the wrong asio configuration # for this arm and declares no dependency on fmt at all, and both are # invisible in the headers. # # WHAT DOES WORK, and why the probe is not a formality. 32 headers compile: the # vocabulary aliases, the UTF-8 checker the WebSocket half validates text frames # with, caseless header-name comparison, the overflow-controlled integer # accumulator every header value is parsed through, bitops, and # incoming_http_msg_limits_t. Section 4 runs them. The UTF-8 checker is a # REFUSER, so the eight inputs it must reject are what prove it -- an # accept-everything checker passes every existence test ever written. # # Fork discipline (the lesson 81-cjson paid for): the runner's per-suite bound is # a budget in PROCESS SPAWNS, not in cases. Each file is read ONCE into a shell # variable and every text assertion is a fork-free `case` glob against it; the # probe runs ONCE with its whole output kept. has/hasnot stay local to this suite # so a hot helper cannot change behaviour under the other suites. inc=/usr/include/restinio cmdir=/usr/lib/cmake/restinio cm=$cmdir/restinio-config.cmake cmt=$cmdir/restinio-targets.cmake cmv=$cmdir/restinio-config-version.cmake PT=/usr/libexec/restinio-tests # has -- $H is the haystack; no fork has() { case "$H" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "not found: $2" ;; esac; } # hasnot -- the assertion is that it is ABSENT hasnot() { case "$H" in *"$2"*) bst_notok "$1" "present but must not be: $2" ;; *) bst_ok "$1" ;; esac; } # --------------------------------------------------------------------------- # 1. Payload. One find, one variable, then globs. require ls restinio/present H=$(find "$inc" -type f 2>/dev/null) n=$(printf '%s\n' "$H" | grep -c .) check_eq restinio/file-count 127 "$n" nh=$(printf '%s\n' "$H" | grep -c '\.hpp$') check_eq restinio/hpp-count 118 "$nh" # The nine .ipp files are NOT a second language: upstream uses the extension for # a body included from a .hpp, so they ship in the same package and a count that # only looked at .hpp would miss a third of impl/. ni=$(printf '%s\n' "$H" | grep -c '\.ipp$') check_eq restinio/ipp-count 9 "$ni" nt=$(printf '%s\n' "$H" | grep -c "^$inc/[^/]*$") check_eq restinio/toplevel-count 41 "$nt" nimpl=$(printf '%s\n' "$H" | grep -c "^$inc/impl/") check_eq restinio/impl-count 23 "$nimpl" nhelp=$(printf '%s\n' "$H" | grep -c "^$inc/helpers/") check_eq restinio/helpers-count 24 "$nhelp" nfp=$(printf '%s\n' "$H" | grep -c "^$inc/helpers/http_field_parsers/") check_eq restinio/field-parser-count 20 "$nfp" nr=$(printf '%s\n' "$H" | grep -c "^$inc/router/") check_eq restinio/router-count 9 "$nr" nu=$(printf '%s\n' "$H" | grep -c "^$inc/utils/") check_eq restinio/utils-count 14 "$nu" nw=$(printf '%s\n' "$H" | grep -c "^$inc/websocket/") check_eq restinio/websocket-count 7 "$nw" # The four bundled nonstd libraries. They ship because RESTINIO_USE_EXTERNAL_*_LITE # are all off, and section 4 asserts that a consumer's string_view_t is one of # THEM rather than std::string_view -- which is the single most surprising thing # about consuming this port. n3=$(printf '%s\n' "$H" | grep -c "^$inc/third_party/") check_eq restinio/third-party-count 4 "$n3" has restinio/hdr-sv-lite "$inc/third_party/string-view-lite/string_view.hpp" has restinio/hdr-opt-lite "$inc/third_party/optional-lite/optional.hpp" has restinio/hdr-exp-lite "$inc/third_party/expected-lite/expected.hpp" has restinio/hdr-var-lite "$inc/third_party/variant-lite/variant.hpp" # The entry points a consumer would name, all present and none of them usable. has restinio/hdr-all "$inc/all.hpp" has restinio/hdr-version "$inc/version.hpp" has restinio/hdr-http-server "$inc/http_server.hpp" has restinio/hdr-http-headers "$inc/http_headers.hpp" has restinio/hdr-settings "$inc/settings.hpp" has restinio/hdr-traits "$inc/traits.hpp" has restinio/hdr-express "$inc/router/express.hpp" has restinio/hdr-websocket "$inc/websocket/websocket.hpp" # And the ones that are. has restinio/hdr-asio-include "$inc/asio_include.hpp" has restinio/hdr-utf8 "$inc/utils/utf8_checker.hpp" has restinio/hdr-caseless "$inc/impl/string_caseless_compare.hpp" has restinio/hdr-accum "$inc/impl/overflow_controlled_integer_accumulator.hpp" has restinio/hdr-limits "$inc/incoming_http_msg_limits.hpp" # The package is headers and a cmake package, and NOTHING else. restinio's cmake # target is an INTERFACE library, so there is no archive, no shared object and no # CLI; an artifact appearing here would mean the recipe started compiling # something. check_rc restinio/no-archive 1 test -f /usr/lib/librestinio.a check_rc restinio/no-sharedlib 1 test -e /usr/lib/librestinio.so check_rc restinio/no-pkgconfig 1 test -f /usr/lib/pkgconfig/restinio.pc # --------------------------------------------------------------------------- # 2. The shipped cmake package -- the assertion this suite exists for as much as # the probe is. # # The image ships no compiler, so nothing on target reads these files. The reader # is a future meta-brintos recipe that DEPENDS on restinio and cross-compiles # against the sysroot; these cases are the only place the contract it will be # handed is checked at all. check restinio/cmake-dir test -d "$cmdir" check restinio/cmake-config test -f "$cm" check restinio/cmake-targets test -f "$cmt" check restinio/cmake-version test -f "$cmv" H=$(cat "$cmt" 2>/dev/null) has restinio/cmake-target 'add_library(restinio::restinio INTERFACE IMPORTED)' has restinio/cmake-cxx14 'INTERFACE_COMPILE_FEATURES "cxx_std_14"' has restinio/cmake-incdir 'INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"' # THE CORRECTED ASIO MACRO SET. Upstream's generated export carries only its own # three (ASIO_STANDALONE, ASIO_HAS_STD_CHRONO, ASIO_DISABLE_STD_STRING_VIEW), so # a consumer taking restinio::restinio would get asio configured the way asio's # OWN port on this layer declares to be broken here: exceptions on, and epoll and # eventfd selected from a kernel version number that does not imply a syscall. # restinio_%.bbappend rewrites the line; asio.pc already carries the same answer # for a pkg-config consumer. A red here means the rewrite stopped taking and the # package went back to promising the upstream three. has restinio/cmake-defs 'INTERFACE_COMPILE_DEFINITIONS "ASIO_STANDALONE;ASIO_HAS_STD_CHRONO;ASIO_DISABLE_STD_STRING_VIEW;ASIO_NO_EXCEPTIONS;ASIO_DISABLE_EPOLL;ASIO_DISABLE_EVENTFD;FMT_HEADER_ONLY"' # restinio's exported interface names fmt::fmt-header-only, an IMPORTED target # rather than a library name, beside http_parser, which IS a bare name and # reaches the linker as -lhttp_parser against this layer's libhttp_parser.a. has restinio/cmake-links 'INTERFACE_LINK_LIBRARIES "fmt::fmt-header-only;http_parser"' # Nothing in the export may name a build-tree path. The BUILD_INTERFACE asio # include directory upstream sets points at dev/asio/include, which the release # tarball does not even contain -- if it ever leaked past the generator expression # into the installed file, every consumer would get a -I into a directory on the # build host. hasnot restinio/cmake-no-buildpath '/home/' hasnot restinio/cmake-no-tmpdir 'tmp-glibc' H=$(cat "$cm" 2>/dev/null) has restinio/cmake-includes-targets 'include(${CMAKE_CURRENT_LIST_DIR}/restinio-targets.cmake)' # THE DECLARED fmt DEPENDENCY. Upstream declares none, so find_package(restinio) # on its own leaves the consumer's generate step failing with `Target links to # target fmt::fmt-header-only but the target was not found`. Added by the # bbappend; a red here is that regression coming back. has restinio/cmake-find-fmt 'find_dependency(fmt)' has restinio/cmake-find-macro 'include(CMakeFindDependencyMacro)' H=$(cat "$cmv" 2>/dev/null) has restinio/cmake-pkg-version 'set(PACKAGE_VERSION "0.6.13")' # write_basic_package_version_file(COMPATIBILITY ExactVersion) writes no such # word -- it emits the comparison. Assert the comparison, which is the behaviour # a consumer gets, rather than a label that is not there. Exact, not # SameMajorVersion: at 0.x upstream makes no compatibility promise and the # generated file says so. has restinio/cmake-exact 'if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)' # The line that distinguishes ExactVersion from SameMajorVersion: the latter # compares PACKAGE_FIND_VERSION_MAJOR against CVF_VERSION_MAJOR and would accept # any 0.6.x. This one compares the whole version less its tweak. has restinio/cmake-no-tweak 'if(REQUESTED_VERSION_NO_TWEAK STREQUAL CVF_VERSION_NO_TWEAK)' # --------------------------------------------------------------------------- # 3. The exceptions gate, as SHIPPED. # # These read the installed headers, not the build tree, so they answer the # question a consumer on this image asks. They are the on-target evidence for a # statement that is otherwise only in a design document. # restinio's OWN code carries no __cpp_exceptions guard at all. The four files # that mention it are the bundled nonstd libraries, which DO discover the # compiler's exceptions state and re-select their contract channel -- so the # guarded and unguarded halves of this package sit in the same include tree, and # the pair of cases below is what separates them. A count that rose would mean # upstream had started guarding, which is the change that would make this port # obsolete and is worth noticing on the day it happens. g=$(grep -rl '__cpp_exceptions' "$inc" 2>/dev/null) ng=$(printf '%s\n' "$g" | grep -c .) check_eq restinio/guard-file-count 4 "$ng" ngo=$(printf '%s\n' "$g" | grep -vc '/third_party/') check_eq restinio/guard-none-of-its-own 0 "$ngo" # The unguarded throw sites, in the two headers where they matter most. # http_headers.hpp is the one every entry point reaches; uri_helpers.hpp is the # one a consumer would expect to be a pure string utility. H=$(cat "$inc/http_headers.hpp" 2>/dev/null) has restinio/throw-http-headers 'throw exception_t{' hasnot restinio/no-guard-http-headers '__cpp_exceptions' H=$(cat "$inc/uri_helpers.hpp" 2>/dev/null) has restinio/throw-uri-helpers 'throw exception_t{' # exception_t itself: a std::runtime_error subclass, which is what makes every # error path above unreachable rather than merely lossy -- there is no # error-code channel behind it to fall back to. H=$(cat "$inc/exception.hpp" 2>/dev/null) has restinio/exception-is-runtime-error 'class exception_t' has restinio/exception-base 'public std::runtime_error' # The include edges that put http_headers.hpp in front of every entry point. A # release that moved the throw out of http_headers.hpp would leave these true and # the throw case above red, which is the right pair of signals; a release that # rearranged the includes reddens here instead. Both are findings. H=$(cat "$inc/all.hpp" 2>/dev/null) has restinio/all-includes-headers '#include ' has restinio/all-includes-server '#include ' has restinio/all-includes-express '#include ' # The thread-pool runner is out on a SECOND, independent count: this libc++ has # no std::thread, so impl/ioctx_on_thread_pool.hpp fails before exceptions are # reached. Asserted through what it uses rather than through a guard it does not # carry, so the case stays true however upstream spells it. H=$(cat "$inc/impl/ioctx_on_thread_pool.hpp" 2>/dev/null) has restinio/threadpool-needs-std-thread 'std::thread' # The three non-std regex engines are alternatives to std_regex_engine, chosen by # the consumer, and each refuses here for its own reason. Asserted so that nobody # later reads their absence from a compile map as one packaging defect, because # they are not one: two are out on the exceptions gate with their dependency # header PRESENT, and only boost's is out for want of a dependency. H=$(cat "$inc/router/pcre_regex_engine.hpp" 2>/dev/null) has restinio/pcre-engine-wants-pcre '#include ' has restinio/pcre-engine-throws 'throw exception_t{' H=$(cat "$inc/router/pcre2_regex_engine.hpp" 2>/dev/null) has restinio/pcre2-engine-wants-pcre2 '#include ' has restinio/pcre2-engine-throws 'throw exception_t{' H=$(cat "$inc/router/boost_regex_engine.hpp" 2>/dev/null) has restinio/boost-engine-wants-boost '#include ' hasnot restinio/boost-engine-no-throw 'throw ' H=$(cat "$inc/router/std_regex_engine.hpp" 2>/dev/null) has restinio/std-engine-wants-std-regex '#include ' # What the image carries for them, measured rather than assumed. The two pcre # headers ARE here, so the pcre engines fail on the same unguarded `throw` as the # rest of the package and not for want of a library; boost's is not, which is the # one genuine dependency gap and is a consumer's choice to close. check_rc restinio/have-pcre-header 0 test -f /usr/include/pcre.h check_rc restinio/have-pcre2-header 0 test -f /usr/include/pcre2.h check_rc restinio/no-boost-regex-header 1 test -f /usr/include/boost/regex.hpp # --------------------------------------------------------------------------- # 4. The probe. Run ONCE; every assertion below is a glob on the captured output. # Each expected value was derived on x86-64 from the same source, built with # the same -std=c++14 -fno-exceptions and the same asio macro set, and is # compared byte-exactly. Exactly two lines are expected to DIFFER from the # x86-64 reference -- P-PTR-BITS and P-LIMITS -- and both are asserted at # their wasm32 values below, with the reason. check restinio/probe-shipped test -f "$PT/restinio-probe" tmp=${TMPDIR:-/tmp} probe=$tmp/bst-restinio-probe.$$ if cp "$PT/restinio-probe" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then H=$("$probe" 2>&1); prc=$? check_eq restinio/probe-rc 0 "$prc" # -- identity, and the two facts every case below rests on has restinio/version 'P-VERSION 0.6.13' has restinio/version-num 'P-VERSION-NUM 6013' has restinio/exceptions-off 'P-EXCEPTIONS 0' has restinio/cxx14 'P-CXXSTD 201402' has restinio/ptr-bits 'P-PTR-BITS 32' has restinio/nodiscard 'P-NODISCARD defined' # -- the asio configuration a restinio consumer INHERITS. restinio does not # wrap asio, it re-exports it, so these six are restinio's configuration # just as much as asio's. The three OFF lines are the syscall table: wasm32 # has no epoll, eventfd or timerfd entry and all three answer -ENOSYS, # while asio would select them from . has restinio/asio-version 'P-ASIO-VERSION 103002' has restinio/asio-standalone 'P-ASIO-STANDALONE 1' has restinio/asio-no-exc 'P-ASIO-NO-EXCEPTIONS 1' has restinio/asio-no-epoll 'P-ASIO-EPOLL 0' has restinio/asio-no-eventfd 'P-ASIO-EVENTFD 0' has restinio/asio-no-timerfd 'P-ASIO-TIMERFD 0' # -- restinio's own two adaptation functions over asio's error codes, which # are the whole of what a handler here could do with an error. Asserted as # a PAIR in both directions: a function that answered true for everything # would pass either line alone. has restinio/asio-eof 'P-ASIO-EOF 1 0' has restinio/asio-aborted 'P-ASIO-ABORTED 0 1' # -- and the link-time obligation behind them. Under ASIO_NO_EXCEPTIONS asio # DECLARES asio::detail::throw_exception and leaves the definition to the # application; every asio entry point that reports failure by throwing goes # through it, error_code's enum-converting constructors included. The probe # supplies one (it reached the link error first, with # `phantom import: env._ZN4asio6detail15throw_exception...`), and that # definition prints P-ASIO-THROWN and exits 97 if it is ever reached. # # So this case is a hasnot: none of the error_code work above may take the # throw path. It is the only assertion here that would still be true if the # probe had died halfway, which is why restinio/probe-complete sits beside # it -- P-END is what says the absence is a result rather than a silence. # # An INTERFACE cmake target can export a macro and an include path and # cannot export a function body, so restinio::restinio will never carry this # for a consumer. It is the one part of using restinio here that every # consumer writes for itself. hasnot restinio/asio-no-throw-taken 'P-ASIO-THROWN' # -- which implementation each vocabulary alias resolved to. At C++14 with the # RESTINIO_EXTERNAL_*_LITE options off these are the bundled nonstd ones, so # a consumer's string_view_t is NOT std::string_view and the two do not # interconvert implicitly. has restinio/alias-nonstd 'P-ALIAS-STRING-VIEW nonstd' has restinio/string-view 'P-SV 12 0' has restinio/optional-empty 'P-OPT-EMPTY 0' has restinio/optional-set 'P-OPT-SET 1 42' # expected-lite with exceptions off reports an error as a VALUE. The pair is # the assertion: the errored one carries its error and the good one its value. has restinio/expected-err 'P-EXPECTED-ERR 0 nope' has restinio/expected-ok 'P-EXPECTED-OK 1 7' has restinio/variant 'P-VARIANT 1' # -- the UTF-8 checker. Five inputs that must be ACCEPTED and eight that must # be REFUSED. The refusals are what prove it: an accept-everything checker # passes every existence check ever written, and this is the code the # WebSocket half validates text frames with. has restinio/utf8-ascii 'P-UTF8 ascii = 1' has restinio/utf8-two 'P-UTF8 two-byte = 1' has restinio/utf8-three 'P-UTF8 three-byte = 1' has restinio/utf8-four 'P-UTF8 four-byte = 1' has restinio/utf8-max 'P-UTF8 max-codepoint = 1' has restinio/utf8-above-max 'P-UTF8 above-max = 0' has restinio/utf8-surrogate 'P-UTF8 surrogate = 0' has restinio/utf8-overlong2 'P-UTF8 overlong-2 = 0' has restinio/utf8-overlong3 'P-UTF8 overlong-3 = 0' has restinio/utf8-lone-cont 'P-UTF8 lone-cont = 0' has restinio/utf8-truncated 'P-UTF8 truncated = 0' has restinio/utf8-bad-cont 'P-UTF8 bad-cont = 0' has restinio/utf8-fe 'P-UTF8 0xFE = 0' # The DECODED value, which an accept/reject check alone would never notice # going wrong: U+1F4A9 is 128169. has restinio/utf8-codepoint 'P-UTF8-CP 128169' # -- caseless header-name comparison through the 256-entry LUT. The high-byte # pair is the one that matters: the LUT is ASCII-only by construction, so # bytes above 0x7F must NOT be folded, and a locale-aware implementation # slipped in later would fold them and this case is the only detector. has restinio/caseless-same 'P-CASELESS same = 1' has restinio/caseless-mixed 'P-CASELESS mixed-case = 1' has restinio/caseless-oneoff 'P-CASELESS one-off = 0' has restinio/caseless-length 'P-CASELESS length = 0' has restinio/caseless-empty 'P-CASELESS empty = 1' has restinio/caseless-high 'P-CASELESS high-byte = 0' # -- the overflow-controlled accumulator, which is how restinio parses every # integer out of a header without trusting the input. Each pair sits at the # boundary: the largest value that fits, and the next one up. has restinio/acc-u16-max 'P-ACC u16-max = ov:0 val:65535' has restinio/acc-u16-over 'P-ACC u16-over = ov:1 val:0' has restinio/acc-u32-max 'P-ACC u32-max = ov:0 val:4294967295' has restinio/acc-u32-over 'P-ACC u32-over = ov:1 val:0' has restinio/acc-hex-max 'P-ACC hex-max = ov:0 val:65535' has restinio/acc-hex-over 'P-ACC hex-over = ov:1 val:0' has restinio/acc-zero 'P-ACC zero = ov:0 val:0' has restinio/acc-leading0 'P-ACC leading0 = ov:0 val:42' # -- bitops, which the WebSocket frame parser reads its length fields with. has restinio/bitops 'P-BITS 18 120 13398' # -- incoming_http_msg_limits_t, and the one place this arm's 32-bit size_t # becomes visible in restinio's own API. Upstream defaults each limit to its # type's maximum, so on x86-64 all four read 18446744073709551615 -- here # the three std::size_t ones narrow to 4294967295 while max_body_size, a # std::uint64_t, does not. The two are asserted TOGETHER because the finding # is that they DISAGREE on this arm: a consumer treating the defaults as one # number gets it wrong by four billion. has restinio/size-t-bits 'P-SIZE-T-BITS 32' has restinio/limits 'P-LIMITS 4294967295 4294967295 4294967295' has restinio/limits-body 'P-LIMITS-BODY 18446744073709551615' has restinio/limits-set 'P-LIMITS-SET 4096' # -- the probe's own tally, and the sentinel that says it ran to the end. A # probe killed halfway prints a prefix of the lines above and would # otherwise contribute only silence. has restinio/probe-failures 'P-FAILURES 0' has restinio/probe-complete 'P-END' else bst_notok restinio/probe-stage "cannot stage $PT/restinio-probe" fi rm -f "$probe" # The probe's SIZE is a functional property here: the guest stages an exec image # as a segment list capped at WASM32_IMG_MAX_SEGS=1024, so a large image is # refused -ENOMEM before it runs (nopoll measured that at 41 MB). asio is the # most template-heavy header set on this image and the probe includes it, so the # ceiling is checked rather than assumed. Bounded from above only -- a probe that # got smaller is not a regression. # BRINTOS-WORKAROUND: brintos/linux#44 revisit -- remove when the segment cap is # raised or removed. sz=$(( $(stat -c %s "$PT/restinio-probe" 2>/dev/null || echo 0) )) if [ "$sz" -gt 0 ] && [ "$sz" -lt 4194304 ]; then bst_ok restinio/probe-size else bst_notok restinio/probe-size "probe is $sz bytes, ceiling 4194304" fi