Preview of main at d0a501a, 2026-11-07

wasi is a header-only helper for C++20 programs built as WebAssembly components on WASI, one of the libraries of webcpp. Its first part, webcpp::wasi::http, is an HTTP handler: a program writes one function, its main, which turns a request’s method and target into a response, and wasi exports the handler that wasmtime serve, or any host of wasi:http, calls, reads the request, calls the main and writes the answer back. It covers WASI 0.2 (wasip2) and WASI 0.3 (wasip3).

This page teaches wasi concept by concept, states what it guarantees, and shows what webcpp’s build does to compile Boost for WASI, with Boost.JSON in a command and in a component. Every program it shows was compiled for wasm32-wasip2 and wasm32-wasip3 and run by wasmtime, and every answer it shows is what that program answered. The reference that closes it lists every name the headers declare.

Introduction

A WebAssembly component is a program that says, in WIT, what it imports from its host and what it exports to it. wasi:http is the WASI interface of HTTP: a host of it, such as wasmtime serve, calls the handler a component exports once for each request it receives, and the handler answers it. The handler’s signature, and every type it reads and writes, are the C functions and structs that wit-bindgen generates from the WIT, and they differ between WASI 0.2 and WASI 0.3.

wasi writes that handler once, for both versions. A program gives it one function, its main, which receives the request’s method and target and returns a response: a status, a content type and a body. Everything between the host and that function, reading the request, freeing it, sending the headers and writing every byte of the body, is wasi’s.

A complete example

A component that greets whoever calls it with GET, and refuses every other method. Its whole source is one file:

#include <webcpp/wasi/http/entrypoint.hpp>

WEBCPP_WASI_HTTP_MAIN_BEGIN(method, target)
    if (method != "GET") {
        return {.status = 405, .content_type = "text/plain", .body = "only GET\n"};
    }
    return {
        .status = 200,
        .content_type = "text/plain",
        .body = "hello from " + std::string(target) + "\n",
    };
WEBCPP_WASI_HTTP_MAIN_END()

The two macros open and close the main, whose parameters are named in the first one: method and target, two std::string_view. The main returns a webcpp::wasi::http::response, written here with designated initializers.

In a b2 project, the component is one line of a Jamfile, which builds it for wasip2 and for wasip3, serves it with wasmtime, sends it requests and compares its answers with a file (Testing):

webcpp.targets wasip2 wasip3 ;
webcpp.serve http_hello.cpp : <library>/webcpp/wasi//http ;

The requests it is sent, one per line, <METHOD> <target>:

GET /v1/greeting?name=ana
POST /x

and what it answered, each request written as the curl command that sends it. curl’s -i also prints the two headers wasmtime adds to every response, date and transfer-encoding, which are left out here:

$ curl -i -X GET 'http://localhost:8080/v1/greeting?name=ana'
HTTP/1.1 200 OK
content-type: text/plain

hello from /v1/greeting?name=ana

$ curl -i -X POST http://localhost:8080/x
HTTP/1.1 405 Method Not Allowed
content-type: text/plain

only GET

The same program builds by hand with wasi-sdk’s clang++, once its bindings are generated: By hand shows the commands.

Requirements

  • A C++20 compiler for WebAssembly: wasi-sdk 34, whose Clang builds wasm32-wasip2 and wasm32-wasip3. The entry point compiles for those two targets alone; the response compiles everywhere, natively too.

  • wit-bindgen’s C generator, 0.62.0 measured, and the wasi:http WIT of the target: the wit/deps directory of the Rust crate wasip2 1.0.4 for WASI 0.2.12, or of wasip3 0.9.0 for WASI 0.3.0.

  • A host of wasi:http, such as wasmtime, 50.0.1 measured.

  • Nothing else. The headers include only the standard library and the bindings wit-bindgen writes, and need no Boost. They throw, try and catch nothing, and use no RTTI: wasi needs neither exceptions nor RTTI, and imposes neither. webcpp builds every program of it with exceptions on wasip2 and wasip3 (Building for WASI with Boost), where an exception that escapes the main traps the instance (Its rules).

Reading this page

<webcpp/wasi.hpp> includes the whole library, which is <webcpp/wasi/http/entrypoint.hpp>: the handler and the macros of the main. <webcpp/wasi/http/response.hpp> holds only the response, for code that needs nothing of the component ABI. Their names are in the namespace webcpp::wasi::http, and what is not public in webcpp::wasi::http::detail.

Your main shows how a program writes its main, and The response what it returns. What your build provides lists what a build gives the header, through b2 or by hand, and wasip3 what changes on WASI 0.3. How a request flows follows one request through the handler, and Limits says what the handler does not do. Guarantees states what wasi promises, each promise with the test or the review that holds it. Building for WASI with Boost shows what webcpp’s build does to compile Boost for WASI, with Boost.JSON in a command and in a component, and Testing how a component is tested, and how wasi is.

Your main

A program’s main is the body written between the two macros, as the complete example’s is (A complete example). WEBCPP_WASI_HTTP_MAIN_BEGIN(method, target) and WEBCPP_WASI_HTTP_MAIN_END() define the function webcpp::wasi::http::http_main, which takes the two std::string_view parameters the first macro names and returns a webcpp::wasi::http::response; and, before it, the extern "C" handler the program’s world exports, which calls it. The handler calls the main once for each request, and sends what it returns.

Parameter What it holds

method

The request’s method as it travels on the wire: "GET", "POST" and the other methods of HTTP, and an extension method, such as "PURGE", as it was sent.

target

The request target, its path and its query, as it was sent: "/v1/greeting?name=ana", with nothing decoded. "/" when the request carried none, and "*" for OPTIONS *.

Its rules

  • Once per program. The pair defines the main and the handler a component exports, so a program writes it once, in one source file. Other translation units of the program may include the header: it holds only inline functions and macros (guarantee 2).

  • The parameters view the request. The handler frees the request once it has written the response, so the response’s content type may view them; the main copies what it keeps beyond the request (guarantee 3).

  • What the response views outlives the main. The content type is a view the handler reads after the main has returned: a string literal, or a view of the method or the target, never of a string of the main’s own, which is gone by then. The body is a std::string, a copy.

  • The main’s body is inside a function of webcpp::wasi::http. It names the program’s own code with its full namespace, or with a namespace alias declared at the top of the body, so that no name of wasi’s hides it.

  • An exception that escapes it is not caught. The handler catches nothing, so on wasip2 and wasip3, where webcpp builds a program with exceptions, an exception that escapes the main traps the instance, and the response is never sent. wasmtime 50.0.1 then prints the trap and its backtrace on its standard error, thrown Wasm exception among them, and answers the client with its own 500 Internal Server Error, the HTML page it answers an internal error with; it serves the requests after it.

  • The target is not parsed. A main that needs the path and the query apart parses the target with a URL library, such as Boost.URL’s parse_origin_form.

Formatting it

clang-format reads the body as a function’s when its configuration names the two macros as the start and the end of a block. These are the two keys of wasi’s own .clang-format that do it:

MacroBlockBegin: "^WEBCPP_WASI_HTTP_MAIN_BEGIN$"
MacroBlockEnd: "^WEBCPP_WASI_HTTP_MAIN_END$"

The response

webcpp::wasi::http::response, in <webcpp/wasi/http/response.hpp>, is what a main returns: an aggregate of three members, each with a default, so that a designated initializer names only what differs from it, as {.status = 404} does.

Member Type What it holds

status

unsigned, 200 by default

The status, sent as it is when it lies within 100 to 599, the five classes of HTTP. Outside them the handler answers an internal error instead of the main’s response (guarantee 9).

content_type

std::string_view, empty by default

The content-type header, sent only when it is not empty. It is read after the main has returned, so it views a string literal, such as "application/json", or the method or the target, never a string of the main’s own. A value the host refuses as a field value, one that holds a CR, an LF or a NUL for example, answers an internal error instead (guarantee 9).

body

std::string, empty by default

The body, sent whole, however long.

The handler sends no other header (guarantee 7).

A response the handler cannot send as the main wrote it, with a status outside 100 to 599 or a content type the host refuses, is not sent at all: the handler answers an internal error, before it has committed anything, and wasmtime 50.0.1 sends that as its own 500 Internal Server Error, an HTML page that names it. Without the check, wasmtime would send some of those statuses as 200 OK, and the status of wasi:http, 16 bits wide, would read 65736 as 200, so a main’s bug would reach the client as a success; and a content type the host refused would be left out without a word.

A content_type is a view rather than a string because it is nearly always a literal: a view of one costs nothing, where a std::string of "application/json", 16 characters, would allocate on wasm32, where libc++ keeps at most 10 in the string itself.

response.hpp includes nothing of the component ABI, and compiles natively too: the rest of a program builds responses, holds them and passes them around, as messages between actors for example, and is tested natively, without a WASI toolchain.

What your build provides

The entry point is only a header, but it leans on what a build gives it: the C bindings of the program’s world, which wit-bindgen generates, and which version of WASI it is built for. A build provides it with these:

What Why

WEBCPP_WASI_HTTP_P2 or WEBCPP_WASI_HTTP_P3 defined, exactly one

It chooses the version. The header stops with #error under neither or both (guarantee 1).

The bindings, generated with --rename-world webcpp_wasi_http

The header includes webcpp_wasi_http.h and uses the names wit-bindgen gives the world, such as webcpp_wasi_http_string_t. The rename makes them the same in every project, whatever its world is called.

webcpp_wasi_http.c, compiled as C for the target, and webcpp_wasi_http_component_type.o, both linked

The glue that calls the handler, and the world’s type, which the component linker reads.

The directory of the bindings on the include path

For webcpp_wasi_http.h.

-mexec-model=reactor on the link

The program is a reactor: it exports a handler and has no C main. Historical tests with wasmtime 47 also served the same program linked without it, as a command. Current webcpp builds and tests reactors.

The host has its part too: wasmtime serve runs a component of wasi with -S cli, since wasi-libc imports parts of wasi:cli, and on wasip3 with -S cli,p3 -W component-model-async (wasip3).

With b2

In a b2 project that uses webcpp, a program that exports the HTTP handler and nothing else links /webcpp/wasi//http, and is done. That target adds wasi’s headers; generates with wit-bindgen the bindings of the standard world of the program’s target, which exports wasi:http/incoming-handler on wasip2 and wasi:http/handler on wasip3, renamed webcpp_wasi_http, and links them; and defines WEBCPP_WASI_HTTP_P2 on the toolset clang-wasip2, or WEBCPP_WASI_HTTP_P3 on clang-wasip3. A component is then an exe with that library and <linkflags>-mexec-model=reactor, which webcpp.serve adds itself, as the complete example’s Jamfile shows:

webcpp.targets wasip2 wasip3 ;
webcpp.serve http_hello.cpp : <library>/webcpp/wasi//http ;

A program whose world exports more than the HTTP handler uses /webcpp/wasi//wasi, the headers alone, generates its own world’s bindings with webcpp.wit-bindings, renamed webcpp_wasi_http too, and defines the macro of its target itself.

b2 looks for each tool only when a target needs it, and a build that needs one that is not there stops, naming it and every place it looked:

Tool Where b2 looks

wit-bindgen

-sWIT_BINDGEN=<path>, else .local/wit-bindgen/wit-bindgen in the superproject, else on PATH.

The wasi:http WIT

-sWASI_WIT_P2=<dir> and -sWASI_WIT_P3=<dir>, each the wit/deps directory of its crate, else .local/wasi-wit/p2 and .local/wasi-wit/p3.

wasmtime

-sWASMTIME=<path>, else on PATH, and only when a component is served.

A user-config.jam may set any of them as -s does, with modules.poke : WIT_BINDGEN : <path> ;. A native build needs none of them.

By hand

The same component builds with wasi-sdk and wit-bindgen alone, from a directory that holds main.cpp, the complete example’s source, and the world the component exports, wit/world.wit, named as the program likes:

package example:hello;

world service {
  export wasi:http/incoming-handler@0.2.12;
}

with wit/deps/ linked to the wasi:http 0.2.12 WIT, the wit/deps directory of the Rust crate wasip2 1.0.4. wit-bindgen generates the bindings with the world renamed webcpp_wasi_http; then wasi-sdk’s clang compiles them as C, and its clang++ compiles and links a reactor, with exceptions on, as webcpp builds every program (Exceptions on wasip2 and wasip3):

"$WIT_BINDGEN" c --world service --rename-world webcpp_wasi_http \
    --out-dir gen wit

"$WASI_SDK/bin/clang" --target=wasm32-wasip2 \
    -c gen/webcpp_wasi_http.c -o gen/webcpp_wasi_http.o -Igen
"$WASI_SDK/bin/clang++" --target=wasm32-wasip2 -std=c++20 \
    -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false \
    -mexec-model=reactor -DWEBCPP_WASI_HTTP_P2 -I"$WASI/include" -Igen \
    main.cpp gen/webcpp_wasi_http.o gen/webcpp_wasi_http_component_type.o \
    -fwasm-exceptions -lunwind -o hello.wasm

WIT_BINDGEN is the wit-bindgen program, WASI_SDK wasi-sdk’s directory and WASI wasi’s. wasmtime serve -S cli hello.wasm then serves the component, which answers the requests of the complete example as its transcript shows.

These are the commands of example/by_hand/build.sh, which the test by_hand of wasi’s examples runs in their lane http on wasip2, with the wasi-sdk, wit-bindgen and WIT the build found, before it serves the component and compares its answers with the complete example’s transcript (Testing a component). A change to them that breaks the build or the answers fails that lane.

wasip3

The same main serves WASI 0.3: only the build differs, and with b2 it differs by the toolset alone, clang-wasip3. By hand:

  • the world exports wasi:http/handler@0.3.0, with the wasi:http 0.3.0 WIT in wit/deps/, the wit/deps directory of the Rust crate wasip3 0.9.0;

  • the bindings are generated with --async 'wasi:http/handler@0.3.0#handle' beside --rename-world;

  • everything is compiled for --target=wasm32-wasip3, the .c of the bindings with -pthread, and the program with -DWEBCPP_WASI_HTTP_P3;

  • wasmtime serves it with -S cli,p3 -W component-model-async.

The world of By hand then exports the handler of 0.3.0:

package example:hello;

world service {
  export wasi:http/handler@0.3.0;
}

and these commands build it, with exceptions on, as webcpp builds every program for wasip2 and wasip3, and as the commands of wasip2 do (Exceptions on wasip2 and wasip3):

"$WIT_BINDGEN" c --world service --rename-world webcpp_wasi_http \
    --async 'wasi:http/handler@0.3.0#handle' --out-dir gen wit

"$WASI_SDK/bin/clang" --target=wasm32-wasip3 -pthread \
    -c gen/webcpp_wasi_http.c -o gen/webcpp_wasi_http.o -Igen
"$WASI_SDK/bin/clang++" --target=wasm32-wasip3 -std=c++20 \
    -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false \
    -mexec-model=reactor -DWEBCPP_WASI_HTTP_P3 -I"$WASI/include" -Igen \
    main.cpp gen/webcpp_wasi_http.o gen/webcpp_wasi_http_component_type.o \
    -fwasm-exceptions -lunwind -o hello.wasm

The test by_hand runs them in its lane on wasip3, and wasmtime serves what they build with the flags above and no other. With exceptions on, an exception that escapes the main traps the instance: the handler catches nothing, and wasmtime 50.0.1 answers the client with its own 500 Internal Server Error (Its rules). Built with -fno-exceptions in place of the exception flags, on the compile and on the link, the component answers the same; webcpp measured that once, by hand, on wasip3, and its lanes build both versions with exceptions.

The handler of wasip3 is lifted asynchronously, but it never gives its task back to the host to wait: it waits for each write inside the task, and always ends with EXIT (guarantee 5). On wasip3, context slot 0 of the component model belongs to wasi-libc, and a handler that resumed in its callback would need it: using it trapped.

How a request flows

The host calls the handler the program exports once per request, and the handler does the same on both versions, in this order:

host (wasmtime serve)
  calls the exported handler
      exports_wasi_http_incoming_handler_handle   (wasip2)
      exports_wasi_http_handler_handle            (wasip3)
    reads the method and the target
    calls the main
      http_main(method, target) -> response
    refuses a status or a content type it cannot send
    sends the status and the content-type
    writes the body, every byte
      wasip2: in chunks of at most 4096 bytes
      wasip3: until the stream has taken it all
    frees the request
  1. It reads the method and the target. A method of HTTP arrives as an enumerator and becomes its name; an extension method arrives as a string, which the main sees as it was sent. The target is the request’s path and query, "/" when it has none.

  2. It calls the main, with two views of the request.

  3. It refuses what it cannot send as written: a status outside 100 to 599, or a content type the host refuses as a field value, answers an internal error, and nothing of the main’s response is sent (guarantee 9).

  4. It sends the response’s head: the status, and the content-type header when content_type is not empty, and no other header (guarantee 7).

  5. It writes the body, every byte (guarantee 4). On wasip2, blocking-write-and-flush takes at most 4096 bytes a call, so the handler writes in chunks of that size, then finishes the body. On wasip3, it writes to the body’s stream until the stream has taken every byte, waiting inside the task when a write blocks, then resolves the trailers future with no trailers.

  6. It frees the request, only once the response is written, so a content type that views the method or the target is valid while it is sent (guarantee 3).

A failure the host reports before the response is sent answers an internal error. Once it is sent, nothing can be taken back: a body not written whole is reported the way each version defines, on wasip2 by dropping the body without finishing it, and on wasip3 by resolving the trailers future with an internal error (guarantee 6).

Complete host messages and streams

Select /webcpp/wasi//host-http and include <webcpp/wasi/http/host.hpp> for incoming and outgoing HTTP with exposed fields, bodies and trailers. This profile imports wasi:http/outgoing-handler on P2 and wasi:http/client on P3, as well as the monotonic clock used for transfer deadlines. It does not require a guest TCP reactor or guest TLS. The existing /webcpp/wasi//http profile and main macros remain available.

incoming_request owns the host request. Read method(), target(), authority() and headers() before consume(error). A body_reader reads into caller-owned spans, enforces transfer_options::body_limit, and reports EOF separately from successful completion. After EOF, call finish to check the completion result and visit the trailers. On P2 the request or response owner must outlive its body reader. On P3 consuming the body moves the request or response and retains its completion future in the reader.

commit_response commits status and fields once and returns a body_writer. Write partial spans with write_some, or a complete span with write, then call finish with optional trailers. Dropping a writer before finishing fails the remaining body. It cannot replace an already committed response. P3 abandonment resolves the mandatory trailers future with an error before releasing the writer; simply dropping that writer would trap in the component ABI.

outgoing_exchange::start takes the scheme, authority, method, path and fields, starts the host operation and returns a request body writer. Finish that writer before receive waits for response headers. The returned incoming_response provides its status, fields and body. The host owns connection reuse, DNS and HTTPS trust. Invalid or forbidden fields return an error; the adapter does not silently switch transports or discard them. message_error retains the pinned interface’s host error discriminant.

The operations above suspend inside the current component task. They are not an Asio completion-token interface. A transfer can use an absolute monotonic deadline or a cancellation predicate. Cancellation is checked between chunks and while waiting, at intervals of at most ten milliseconds, except when the host itself blocks inside an imported operation. The adapter does not allocate C++ operation state. Canonical ABI strings and lists still use the generated bindings’ cabi_realloc; caller-owned bodies and fields use their own allocator.

Nonblocking body and response operations

<webcpp/wasi/http/poll.hpp> exposes read_task, write_task and response_task. Each poll performs one bounded readiness step and returns false while the host operation remains pending. The tasks own their body resources and, on P3, stable storage for pending stream and future results. A pending read or write borrows its payload span until a subsequent poll completes; the caller must keep that span unchanged and alive. Destroying a task cancels pending host access before releasing resources or storage.

read_task::finish polls body completion and incoming trailers after EOF. write_task::finish polls outgoing trailers after the final content chunk. response_task::poll waits for response headers without blocking on the network. P2 future tasks retain their readiness subscriptions for the entire pending interval. These primitives allocate no C++ operation state and can be driven by a component event loop. Beast’s explicit WASI asynchronous adapter builds completion-token operations over them.

Standard CLI streams

<webcpp/wasi/stdio.hpp> exposes stdio_channel over the pinned wasi-sdk CLI stream bindings on P2 and P3. Each operation returns a partial byte count and ready, blocked, eof or failed. The object owns the acquired stream resources; it neither creates sockets nor interprets protocol bytes. No caller buffer remains borrowed after a partial operation returns.

The direct CLI adapter avoids wasi-sdk 34’s stdio descriptors, which reject fcntl(F_SETFL) and always request blocking I/O. Wasmtime 50.0.1 can still block inside a standard-output host write when its OS pipe fills. Guest cancellation is then observed after the peer resumes draining. The tests bound that pause and resume draining concurrently; they do not claim that the guest can interrupt a blocking host import. Diagnostics belong on stderr, separately from the protocol on stdout.

Limits

The original main-macro handler hands the main only the method and the target. It reads no request header and no request body, and sends no response header but content-type. Use the complete host-message API for a request body, additional fields, trailers or an outgoing host request.

What it allocates

The handler’s own code allocates nothing: it views the request’s strings where they are, and the response’s content type where the main put it. Two things do allocate, each through its language’s default:

  • The request’s strings. The target, and an extension method’s name, are copied into the component’s memory as the host hands them over, through the cabi_realloc the bindings define, which calls wasi-libc’s realloc. The handler frees them once it has written the response.

  • The response’s body, a std::string, which allocates through std::allocator once it outgrows the 10 characters it holds in itself on wasm32.

What the main allocates besides is the program’s own. A program cannot yet give wasi an allocator of its own for either.

Guarantees

What wasi promises whatever its program does within the rules of its main (Its rules), and what holds each promise: a test of wasi’s, which fails when the promise breaks, or review, where no test can make the host misbehave on demand. A guarantee keeps its number, and the anchor named after it, so a comment of the code or of a test cites it by that anchor: (doc: #wasi-invariant-4) sends its reader to the fourth. b2 libs/wasi/doc, which builds this page, fails on a citation that names no anchor of it. The tests are those of How wasi is tested.

  1. The entry point compiles only for WASI, and for one version. It includes the webcpp_wasi_http.h of the program’s world, and refuses to compile unless exactly one of WEBCPP_WASI_HTTP_P2 and WEBCPP_WASI_HTTP_P3 is defined. response.hpp compiles anywhere. Held by neither_target_refused and both_targets_refused, which compile only with the guard’s own error, on wasip2 and on wasip3; by the public headers compiled alone, the entry point on wasip2 and wasip3, and the response there and natively; and by response, run natively and on both versions.

  2. The header defines no function of its own that a program links. The exported extern "C" functions are defined by the expansion of WEBCPP_WASI_HTTP_MAIN_BEGIN, in the program; the header holds only inline functions and macros, so every translation unit of a program may include it, and the program writes the pair of macros once. Held by http_two_units, a component of two translation units that both include the entry point, which links and answers.

  3. Nothing written back points into freed memory. The handler frees the method and the target, and drops the request, only after the response is written, so a content type that views the method or the target is still valid when it is sent. The response owns its body, a copy of what the main built it from, so no body views the request. Held by http_content_type_of_target, whose content type views the target, on wasip2 and on wasip3, which fails when the target is freed before the content type is read; and by response, which checks that the body is a copy.

  4. Every byte of the body is written. On wasip2 in chunks of at most 4096 bytes, the most blocking-write-and-flush takes; on wasip3 by writing until the stream has taken every byte. Held by http_long_body, whose body of three chunks and some bytes more arrives whole, on wasip2 and on wasip3. Historical tests with wasmtime 47 took the whole body in one write on wasip3, a body of 1 MiB too. The regression with wasmtime 50.0.1 verifies complete delivery, not the number of writes. The loop stands for a host that takes less per write; the test fails when a write is cut short and the remaining bytes are not sent.

  5. On wasip3, the handler never resumes in its callback. It waits inside the task for every write that blocks, and always ends with EXIT, because context slot 0 belongs to wasi-libc on wasip3: using it trapped. Held by http_long_body on wasip3, and every component served there, and by review.

  6. A failure is reported the way the version defines. One found before the response is committed answers an internal error. After it is committed, a body not written whole is dropped without being finished on wasip2, and on wasip3 the trailers future is resolved with an internal error. Held by review: wasmtime offers no way to make a write fail on demand.

  7. The response carries a content-type header only when content_type is not empty, and no other header. Held by http_no_content_type, whose answers carry no header for an empty content type and exactly one for a set one.

  8. wasi names nothing of the programs that use it. The only generated names it uses are those of the standard interfaces and those --rename-world webcpp_wasi_http makes. Held by the lint’s rule on the banned word, which refuses the name of the program wasi was first written for in every file, file name and commit, and by review.

  9. A response that cannot be sent as the main wrote it is refused, not altered. A status outside 100 to 599, and a content type the host refuses as a field value, are not sent: the handler answers an internal error instead, before anything is committed, on both versions. Held by http_bad_status, whose statuses 0, 99, 600 and 65736 answer wasmtime’s 500 Internal Server Error, and 599 is sent as it is; and by http_bad_content_type, whose content types with a CR and an LF, and with a NUL, answer the same, and one with a tab is sent as it is; on wasip2 and on wasip3.

Building for WASI with Boost

wasi itself needs no Boost, but a component rarely stops at its handler: it parses JSON, computes, keeps state. webcpp builds every library it holds for wasm32-wasip2 and wasm32-wasip3, Boost included, and this chapter shows what its build does for that, so that a project of its own can do the same. Every listing below is the build’s own configuration, included from the file the build reads, or a program webcpp compiles and runs on both targets on every build.

Boost.JSON, Boost.System, Boost.Decimal and Boost.MSM build and run on both targets in webcpp’s lanes, as the tests and the examples of its libraries use them; Boost.JSON shows the first in a command and in a component.

The toolsets

The WASI toolsets are b2’s clang, registered against wasi-sdk 34 under the versions wasip2 and wasip3, so that toolset=clang-wasip2 and toolset=clang-wasip3 name them. A user-config.jam registers them with these lines, the first two of which name wasi-sdk’s directory and give it to the build, which builds the component of a script with it (How wasi is tested); the CI writes them into the user-config of each of its WASI lanes, with the directory where it installed wasi-sdk:

local wasi-sdk = /path/to/wasi-sdk ;
modules.poke : WASI_SDK : $(wasi-sdk) ;

using clang : wasip2 : $(wasi-sdk)/bin/clang++
  : <cflags>--target=wasm32-wasip2 <cxxflags>--target=wasm32-wasip2
    <linkflags>--target=wasm32-wasip2
    <archiver>$(wasi-sdk)/bin/llvm-ar <ranlib>$(wasi-sdk)/bin/llvm-ranlib ;

using clang : wasip3 : $(wasi-sdk)/bin/clang++
  : <cflags>--target=wasm32-wasip3 <cxxflags>--target=wasm32-wasip3
    <linkflags>--target=wasm32-wasip3
    <archiver>$(wasi-sdk)/bin/llvm-ar <ranlib>$(wasi-sdk)/bin/llvm-ranlib ;

Each gives the target to the compilers and to the linker, and makes archives with wasi-sdk’s own llvm-ar and llvm-ranlib.

wasm32-wasip2 is no system b2 knows. webcpp’s Jamroot declares a target system wasi of its own, whose programs end in .wasm:

if ! wasi in [ feature.values <target-os> ]
{
    feature.extend target-os : wasi ;
}
type.set-generated-target-suffix EXE : <target-os>wasi : wasm ;

It declares it after the toolsets of the user-config.jam have loaded, so that none of the Unix link options that b2 gives the systems it knows, such as --start-group or an rpath, reaches wasi-sdk’s linker, which rejects them. Every target of the two toolsets is then built for that system, and linked statically, since WASI has no shared libraries:

<toolset>clang-wasip2:<target-os>wasi
<toolset>clang-wasip3:<target-os>wasi
<target-os>wasi:<link>static

A program built so runs with wasmtime, which b2 starts for a test or an example given testing.launcher=wasmtime on its command line, beside toolset=clang-wasip2 or toolset=clang-wasip3 (Targets).

Exceptions on wasip2 and wasip3

A program may use exceptions on wasip2 and wasip3, as natively: whether it does is its developer’s choice, and webcpp builds every program for both with them on, encoded as WebAssembly’s exception handling:

<toolset>clang-wasip2,<exception-handling>on:<cxxflags>"-fwasm-exceptions -mllvm -wasm-use-legacy-eh=false"
<toolset>clang-wasip2,<exception-handling>on:<linkflags>"-fwasm-exceptions -lunwind"
<toolset>clang-wasip3,<exception-handling>on:<cxxflags>"-fwasm-exceptions -mllvm -wasm-use-legacy-eh=false"
<toolset>clang-wasip3,<exception-handling>on:<linkflags>"-fwasm-exceptions -lunwind"

-fwasm-exceptions encodes a throw and a catch as WebAssembly instructions, on the compile and on the link, and -lunwind links the runtime that unwinds the stack. -mllvm -wasm-use-legacy-eh=false chooses which instructions: the Clang of wasi-sdk 34 emits by default the legacy encoding of the proposal’s first version, and wasmtime 47 refuses to run a program that uses it, saying legacy_exceptions feature required. With the flag, Clang emits the standard encoding, exnref, which wasmtime runs, and serves in a component, with no flag of its own.

A program that uses exceptions needs a host that runs that encoding, which WebAssembly 3.0 made standard: wasmtime 47 or later, or wasmtime 37 to 46 given -W exceptions=y; jco, whose jco transpile is given --bindgen-enable-wasm-exnref; or another engine of WebAssembly 3.0.

wasi’s handler catches nothing: an exception that escapes the main traps the instance, and wasmtime 50.0.1 answers the client with its own 500 Internal Server Error (Its rules). A main that may throw catches what it can answer itself.

A user’s build without exceptions, exception-handling=off, compiles with -fno-exceptions alone, under which Boost.Config defines BOOST_NO_EXCEPTIONS, and that asks something of the program. Where Boost would throw, it calls boost::throw_exception, which its headers declare and leave to the program to define: a program built without exceptions that calls it, as Boost.System’s result and Boost.JSON do, does not link without a definition. webcpp’s tools/throw_exception.cpp defines both overloads: each prints what was thrown on the standard error, and aborts, since a throw in such a build is a defect, never a condition. webcpp’s build links it into every program a user builds with exception-handling=off, on any target. A program built by hand without exceptions compiles with -fno-exceptions -DBOOST_NO_EXCEPTIONS, and compiles and links that file, or a definition of its own.

So a program that may be built without exceptions asks Boost for errors as values. The command of Boost.JSON parses with an error_code, which a broken text sets, and reads the value with if_object and if_contains, which answer a null pointer for what is not there; as_object and at would report it by throwing, which in such a build ends the program.

<webcpp/wasi/config.hpp>, which every public header of wasi includes first, defines WEBCPP_WASI_NO_EXCEPTIONS when the build has no exceptions or when the program defines WEBCPP_NO_EXCEPTIONS, which turns off the exceptions of every webcpp library at once, and a program may define it for wasi alone, to turn wasi’s exceptions off in a build that has them, as Boost.Asio’s BOOST_ASIO_NO_EXCEPTIONS does. wasi reads WEBCPP_NO_EXCEPTIONS itself, since it includes nothing of Boost, nor <webcpp/config.hpp>, which includes Boost.Config. wasi raises no exception of its own, so the macro changes nothing in it: it exists so that a program can set it for every webcpp library alike. The tests config, config_developer_defined and config_all_off check the three ways it is defined, natively and on both targets, and the lint refuses a throw in a public header, even in a template nothing instantiates: an exception goes through boost::throw_exception.

Boost.JSON

Boost.JSON is not only headers: its definitions are compiled once in a program, from the translation unit that includes <boost/json/src.hpp>. webcpp builds that translation unit as the static library /webcpp//boost_json, once for each variant and target, so that the program’s own features, such as <exception-handling>off, reach it; a program links it with <library>/webcpp//boost_json, and needs no compiled Boost library. wasi’s two programs of this chapter do so:

webcpp.example boost_json_command.cpp : <library>/webcpp//boost_json ;
webcpp.serve boost_json_http.cpp
  : <library>/webcpp/wasi//http <library>/webcpp//boost_json ;

A command

A WASI command is a program with a main, which wasmtime runs as it runs a program of the system. This one parses a JSON text, reads what it holds and prints it. It includes Boost.JSON, and Boost.System for the error code, and calls Boost.JSON’s namespace json:

#include <boost/json.hpp>
#include <boost/system/error_code.hpp>

#include <iostream>
#include <string_view>

namespace json = boost::json;

The text is a literal, so the program reads no input and prints the same on every target:

// What a component could be configured with: its name, its routes and its limits.
constexpr std::string_view config = R"({
    "name": "greeter",
    "routes": ["/v1/greeting", "/v1/health"],
    "limits": {"body": 65536, "debug": false}
})";

main parses it with an error code, reads its name and its routes, changes one of its limits and prints the limits, then parses a broken text:

int main() {
    boost::system::error_code error;
    json::value parsed = json::parse(config, error);
    json::object* object = parsed.if_object();
    if (error || object == nullptr) {
        std::cout << "not a configuration: " << error.message() << '\n';
        return 1;
    }
    if (const json::value* name = object->if_contains("name")) {
        std::cout << "name: " << json::serialize(*name) << '\n';
    }
    if (const json::value* routes = object->if_contains("routes");
        routes != nullptr && routes->is_array()) {
        for (const json::value& route : routes->get_array()) {
            std::cout << "route: " << json::serialize(route) << '\n';
        }
    }
    if (json::value* limits = object->if_contains("limits");
        limits != nullptr && limits->is_object()) {
        limits->get_object()["debug"] = true;
        std::cout << "limits: " << json::serialize(*limits) << '\n';
    }

    const json::value broken = json::parse(R"({"name": })", error);
    std::cout << "a broken text: " << error.message() << ", read as " << json::serialize(broken)
              << '\n';
}

It prints, on wasip2 and on wasip3:

name: "greeter"
route: "/v1/greeting"
route: "/v1/health"
limits: {"body":65536,"debug":true}
a broken text: syntax error, read as null

A component

A component that answers JSON: the list of a catalog’s items, and one item by its id. It includes wasi’s entry point and Boost.JSON:

#include <webcpp/wasi/http/entrypoint.hpp>

#include <boost/json.hpp>

#include <array>
#include <charconv>
#include <cstdint>
#include <string_view>
#include <utility>

The catalog is the program’s own code, in a namespace of its own, which an anonymous one keeps to this file: its items, an item as a JSON object, the item a target names, and an answer whose body Boost.JSON writes:

namespace {

namespace catalog {

struct item {
    std::int64_t id;
    std::string_view name;
};

constexpr std::array items{item{.id = 1, .name = "apple"}, item{.id = 2, .name = "pear"}};

boost::json::object json_of(const item& entry) {
    return {{"id", entry.id}, {"name", entry.name}};
}

// The item a target /v1/items/<id> names, or nullptr.
const item* find(std::string_view target) {
    constexpr std::string_view prefix = "/v1/items/";
    if (!target.starts_with(prefix)) {
        return nullptr;
    }
    const std::string_view digits = target.substr(prefix.size());
    std::int64_t id = 0;
    const auto [end, error] = std::from_chars(digits.data(), digits.data() + digits.size(), id);
    if (error != std::errc{} || end != digits.data() + digits.size()) {
        return nullptr;
    }
    for (const item& entry : items) {
        if (entry.id == id) {
            return &entry;
        }
    }
    return nullptr;
}

webcpp::wasi::http::response answer(unsigned status, const boost::json::value& body) {
    return {
        .status = status,
        .content_type = "application/json",
        .body = boost::json::serialize(body) + "\n",
    };
}

}  // namespace catalog

}  // namespace

The main refuses every method but GET, lists the items, or answers the one the target names, or that there is none:

WEBCPP_WASI_HTTP_MAIN_BEGIN(method, target)
    if (method != "GET") {
        return catalog::answer(405, boost::json::object{{"error", "only GET"}});
    }
    if (target == "/v1/items") {
        boost::json::array all;
        for (const catalog::item& entry : catalog::items) {
            all.push_back(catalog::json_of(entry));
        }
        return catalog::answer(200, boost::json::object{{"items", std::move(all)}});
    }
    if (const catalog::item* entry = catalog::find(target)) {
        return catalog::answer(200, catalog::json_of(*entry));
    }
    return catalog::answer(404, boost::json::object{{"error", "no such item"}, {"target", target}});
WEBCPP_WASI_HTTP_MAIN_END()

wasmtime serves it these requests:

GET /v1/items
GET /v1/items/2
GET /v1/items/9
GET /v1/items/2x
DELETE /v1/items/2

and it answers them so, on wasip2 and on wasip3, the headers wasmtime adds left out:

$ curl -i -X GET http://localhost:8080/v1/items
HTTP/1.1 200 OK
content-type: application/json

{"items":[{"id":1,"name":"apple"},{"id":2,"name":"pear"}]}

$ curl -i -X GET http://localhost:8080/v1/items/2
HTTP/1.1 200 OK
content-type: application/json

{"id":2,"name":"pear"}

$ curl -i -X GET http://localhost:8080/v1/items/9
HTTP/1.1 404 Not Found
content-type: application/json

{"error":"no such item","target":"/v1/items/9"}

$ curl -i -X GET http://localhost:8080/v1/items/2x
HTTP/1.1 404 Not Found
content-type: application/json

{"error":"no such item","target":"/v1/items/2x"}

$ curl -i -X DELETE http://localhost:8080/v1/items/2
HTTP/1.1 405 Method Not Allowed
content-type: application/json

{"error":"only GET"}

What does not build

Boost.Asio 1.92 does not compile for wasm32-wasip2 or wasm32-wasip3 with wasi-sdk 34: wasi-libc defines no ESHUTDOWN and no ::pause, and its signal.h stops with #error. It was measured when webcpp’s first library, xactor, was ported: its Asio driver is built natively alone, and xactor records why in its example Jamfile and on its page.

Testing

A component is a program that only a host runs, so its test runs one: it builds the component, has wasmtime serve it, sends it requests and compares its answers with the ones committed beside it. What the main computes needs no host. The response compiles natively, and a main that calls code of the program’s own, as the catalog of A component does, leaves that code to be tested natively, as any other.

Testing a component

In a b2 project that uses webcpp, a served test is one line of a test or example Jamfile, as the complete example’s is:

webcpp.targets wasip2 wasip3 ;
webcpp.serve http_hello.cpp : <library>/webcpp/wasi//http ;

webcpp.serve <source> : <requirements> ; declares the test <stem>, which builds the source as a component for wasip2 and for wasip3, the Jamfile’s targets among them. It serves the component with wasmtime serve, with -S cli on wasip2 and -S cli,p3 -W component-model-async on wasip3, on a port the system chooses, so that two lanes at once never share one. It then sends it each line of <stem>.requests, <METHOD> <target>, and passes when the transcript of the answers equals <stem>.expected, byte for byte; otherwise it fails with the difference. It is a test of b2’s, which --dump-tests lists and --out-xml records, so a failure shows in the test matrix. The explicit target <stem>-component is the same component, to serve by hand.

The transcript writes each request as the curl command that sends it, which a shell runs as it is written:

  • curl -i -X <METHOD> http://localhost:8080<target>, each word quoted where a shell would read it otherwise, as the target ?name=ana is;

  • curl -I <url> for HEAD, since curl -X HEAD waits for a body that never comes;

  • --request-target '*' before the bare origin for a target that is not a path, the * of OPTIONS *.

Then come the status line, HTTP/1.1 <status> <reason>, the response’s headers in lower case and sorted, an empty line and the body as it came. The headers wasmtime adds to every response, date and transfer-encoding, are left out, and the host and the port are written fixed, so that one transcript holds on every machine and every lane. Two requests are separated by an empty line. tools/component/serve.py, in the superproject, writes the transcript and holds its format.

A served test runs in a lane of its own, which webcpp.lane declares with the targets it runs on, as the complete example’s is:

webcpp.lane http : http_hello by_hand boost_json_http
  : wasip2 wasip3 ;

The lane is the explicit target http of its Jamfile, and the tests it names become explicit too: b2 on the test and example directories leaves them out, and b2 libs/wasi/example//http toolset=clang-wasip2 testing.launcher=wasmtime runs them. The continuous integration runs the lane once per target it names, as it runs that target’s lane, and its tests reach the test matrix in a column of its own.

How wasi is tested

wasi’s tests are under test/, each declared in test/Jamfile, and each guarantee of Guarantees names the tests that hold it:

Test What it checks

response

A lightweight_test program: the response’s defaults, a designated initializer that names one member, and that the body is a copy. Run natively, on wasip2 and on wasip3.

http_methods

A served component: GET, HEAD, POST, PUT, DELETE, OPTIONS, TRACE, PATCH and the extension method PURGE reach the main by name, and targets with a query, an escape, / and * reach it as they were sent. A HEAD answer carries no body.

http_long_body

A served component whose body is three chunks of 4096 bytes and some more, which must arrive whole, every numbered line of it.

http_no_content_type

A served component: an empty content type sends no header, with a body and without one, and a set one sends exactly one.

http_two_units

A served component of two translation units that both include the entry point, which links and answers.

http_content_type_of_target

A served component whose content type views the target, which must arrive as the target reads, on wasip2 and on wasip3.

http_bad_status

A served component that answers the status its target names: 0, 99, 600 and 65736 must answer wasmtime’s 500 Internal Server Error, and 599 and 200 arrive as they are.

http_bad_content_type

A served component whose content types hold a CR and an LF, and a NUL, which must answer wasmtime’s 500 Internal Server Error, and a tab, which arrives as it is.

neither_target_refused, both_targets_refused

A translation unit that defines neither version’s macro, and one that defines both, each compiled and passing only on the guard’s own error, on wasip2 and on wasip3.

Beside them, test/Jamfile compiles each of the 8 public headers alone, as the only include of its translation unit, so that a header that leans on an include it does not make fails, naming it: the entry point and <webcpp/wasi.hpp> on wasip2 and wasip3, with the bindings and the macro of each, and the response natively too. The seven served components run in the lane http of test/Jamfile. example/Jamfile serves, in a lane http of its own, the complete example and the component of A component, and runs the command of A command, comparing what it prints with the .expected beside it. Its test by_hand, in the same lane, runs the commands of By hand and wasip3, example/by_hand/build.sh, and serves the component they build, which must answer as the complete example does. webcpp.serve-script declares it, with the script and the stem of the requests and the transcript it shares with the complete example:

webcpp.serve-script by_hand : by_hand/build.sh : http_hello ;

The script is run as build.sh <p2|p3> <component>, with the tools b2 found for the lane in its environment: WASI_SDK, the directory of wasi-sdk that the user-config gives (The toolsets), else .local/wasi-sdk, WIT_BINDGEN, and WASI_WIT, the WIT of the lane’s version.

Targets

wasi’s programs are built for the targets their Jamfiles declare: every component, and the command, on wasip2 and wasip3; the response, and its header alone, natively as well. Two requests run the 24 tests of wasip2, and two the 24 of wasip3: the served ones in the lanes http, the others with the test and example directories. From the root of a webcpp checkout:

b2 libs/wasi/test libs/wasi/example toolset=clang-wasip2 testing.launcher=wasmtime

Builds and runs, for wasm32-wasip2 and with exceptions, every test that is not served, and the command, which wasmtime runs. toolset=clang-wasip3 does the same for wasm32-wasip3.

b2 libs/wasi/test//http libs/wasi/example//http toolset=clang-wasip2 testing.launcher=wasmtime

Runs the lanes http of the tests and of the examples: builds the 10 components for wasm32-wasip2 and has wasmtime serve each. toolset=clang-wasip3 does the same for wasm32-wasip3: 10 components. Without a WASI toolset, the request stops, naming the toolsets to give, rather than build nothing.

b2 libs/wasi/test libs/wasi/example

Builds and runs, natively, the 6 tests that need no WASI toolchain, and needs none of the tools below.

The WASI toolsets are those of The toolsets. b2 compares timestamps, so a result that counts is built from scratch, with b2 -a; b2 libs/wasi/test//http_methods toolset=clang-wasip2 testing.launcher=wasmtime builds and runs one test alone, while working on it.

The tools a machine needs

A WASI build needs, besides b2 and Boost 1.92:

Tool What for

wasi-sdk 34

The Clang that compiles and links for wasm32-wasip2 and wasm32-wasip3, registered as in The toolsets.

wit-bindgen 0.62.0

Generates the C bindings of each world, when a component is built.

The wasi:http WIT of WASI 0.2.12 and 0.3.0

The wit/deps directories of the Rust crates wasip2 1.0.4 and wasip3 0.9.0, which the bindings are generated from.

wasmtime 50.0.1

Runs every test and example, and serves each component.

Where b2 looks for each is in With b2; a WASI build that needs one that is not there stops, naming it and every place it looked.

The test matrix

The continuous integration runs wasi’s tests and examples in lanes, one toolset each: the native compilers, and wasip2 and wasip3. Each lane builds from scratch and records what b2 built and ran:

b2 -a --dump-tests --out-xml=<lane>.xml toolset=<toolset> \
    libs/wasi/test libs/wasi/example

The lanes http of the tests and of the examples run together, the same way, on wasip2 and on wasip3, with the target’s toolset, and record their run under a name of its own, made of the target, the library and the lane: wasip2.wasi.http for the served tests and examples on wasip2, wasip3.wasi.http on wasip3:

b2 -a --dump-tests --out-xml=wasip2.wasi.http.xml \
    toolset=clang-wasip2 testing.launcher=wasmtime \
    libs/wasi/example//http libs/wasi/test//http

webcpp’s tools/report/report.py merges the files of every lane into a matrix: a page of the libraries by lanes, and a page of each library’s tests and examples by lanes, the served ones in the columns of the lanes http, where each cell says whether that program passed in that lane, or how it failed, at compile time, at link time or when it ran. With --out-xml, b2 exits 0 even when a test fails, so the report is the verdict. The matrix is published with the documentation, at https://webcpporg.github.io/report/.

Reference

Every name wasi declares, in the namespace webcpp::wasi::http, and its macros, from the Doc Comments of its headers: each with its declaration, what it does and what it asks of its caller. It is read with the bindings of wasip2 and WEBCPP_WASI_HTTP_P2, so the macro WEBCPP_WASI_HTTP_EXPORTS shows the handler of wasip2; wasip3 says what differs on wasip3.

Macros

Name Description

WEBCPP_WASI_HTTP_EXPORTS

Defines the extern "C" handler the target’s world exports, which hands each request to the library’s handler.

WEBCPP_WASI_HTTP_MAIN_BEGIN

Opens the definition of the program’s main, after the handler the target’s world exports.

WEBCPP_WASI_HTTP_MAIN_END

Closes the definition of the program’s main that WEBCPP_WASI_HTTP_MAIN_BEGIN opened.

WEBCPP_WASI_NO_EXCEPTIONS

Defined when wasi is built without exceptions: automatically when the compiler has none (where Boost.Config defines BOOST_NO_EXCEPTIONS), when the developer defines WEBCPP_NO_EXCEPTIONS for every webcpp library, or when the developer defines it alone, to disable wasi’s in a build that has them.

webcpp::wasi

Namespaces

Name

http

Types

Name Description

stdio_channel

Owns standard WASI CLI streams.

stream_result

Bytes copied by a partial stream operation, independent of any wire protocol.

Enums

Name Description

stream_status

The observable result of a partial host stream operation.

webcpp::wasi::http

Types

Name Description

body_reader

A host body reader, with bounded partial I/O and explicit trailers completion.

body_writer

A committed body writer.

incoming_request

An incoming host request.

incoming_response

A response received from the host client, including its body resource lifetime.

message_error

An operation failure, retaining the pinned wasi:http error-code discriminant.

message_fields

Owning HTTP fields.

outgoing_exchange

An outgoing exchange.

read_task

Owns a nonblocking body read and its pending completion resources.

response

What the program’s main answers one HTTP request with: a status, a content type and a body.

response_task

Polls an outgoing exchange’s response without waiting for network readiness.

transfer_options

Limits shared by a transfer.

write_task

Owns a nonblocking body write and stable pending trailer storage.

Type Aliases

Name Description

request_handle

Owned request resource of the selected WASI ABI.

response_out

P2 response commitment resource.

Enums

Name Description

message_errc

Errors of a host message operation, independent of a C++ exception runtime.

Functions

Name Description

commit_response

Commits response headers once and returns its body writer.

http_main

Answers one HTTP request, given its method and its target: the program’s main.

refuse_response

Reports a pre-commit error.

webcpp::wasi::http::request_handle

Owned request resource of the selected WASI ABI.

Synopsis

using request_handle = wasi_http_types_own_incoming_request_t;

webcpp::wasi::http::response_out

P2 response commitment resource.

Synopsis

using response_out = wasi_http_types_own_response_outparam_t;

webcpp::wasi::http::body_reader

A host body reader, with bounded partial I/O and explicit trailers completion.

Synopsis

class body_reader;

Member Functions

Name Description

body_reader [constructor]

Constructors

~body_reader [destructor]

Fails and releases any unfinished body.

operator= [deleted]

Forbids replacing a potentially unfinished body.

eof

Whether the content stream has reached its end; finish still checks body failure.

read_some

Reads at most the supplied span.

finish

Resolves body completion, then visits trailers.

Friends

Name Description

webcpp::wasi::http::read_task

Owns a nonblocking body read and its pending completion resources.

webcpp::wasi::http::body_reader::body_reader

Constructors

Synopses

Constructs an empty reader.

constexpr
body_reader() = default;

Transfers body ownership.

body_reader(body_reader&&) = default;

Takes ownership of a P2 incoming body.

explicit
body_reader(wasi_http_types_own_incoming_body_t body);

Parameters

Name Description

body

Incoming body resource.

webcpp::wasi::http::body_reader::body_reader

Constructs an empty reader.

Synopsis

constexpr
body_reader() = default;

webcpp::wasi::http::body_reader::body_reader

Transfers body ownership.

Synopsis

body_reader(body_reader&&) = default;

webcpp::wasi::http::body_reader::body_reader

Takes ownership of a P2 incoming body.

Synopsis

explicit
body_reader(wasi_http_types_own_incoming_body_t body);

Parameters

Name Description

body

Incoming body resource.

webcpp::wasi::http::body_reader::~body_reader

Fails and releases any unfinished body.

Synopsis

~body_reader();

webcpp::wasi::http::body_reader::operator=

Forbids replacing a potentially unfinished body.

Synopsis

body_reader&
operator=(body_reader&&) = delete;

webcpp::wasi::http::body_reader::eof

Whether the content stream has reached its end; finish still checks body failure.

Synopsis

[[nodiscard]]
bool
eof() const noexcept;

Return Value

Whether the body content stream has ended.

Note

The return value should not be discarded.

webcpp::wasi::http::body_reader::read_some

Reads at most the supplied span.

Synopsis

std::size_t
read_some(
    std::span<char> output,
    transfer_options const& options,
    message_error& error);

Return Value

The number of bytes transferred before completion or failure.

Parameters

Name Description

output

Destination bytes.

options

Body limit, cancellation and deadline.

error

Receives any failure.

webcpp::wasi::http::body_reader::finish

Resolves body completion, then visits trailers.

Synopsis

template<class Visitor>
message_error
finish(
    Visitor&& visitor,
    transfer_options const& options);

Description

Call after EOF.

Return Value

An empty error value on success, otherwise the failure.

Template Parameters

Name Description

Visitor

Callable accepting trailer name and value views.

Parameters

Name Description

visitor

Callback for the received trailers.

options

Cancellation and deadline while waiting for trailers.

webcpp::wasi::http::body_writer

A committed body writer.

Synopsis

class body_writer;

Description

Destruction without finish fails the body, never commits again.

Member Functions

Name Description

body_writer [constructor]

Constructors

~body_writer [destructor]

Fails and releases any unfinished outgoing body.

operator= [deleted]

Forbids replacing a potentially unfinished body.

write_some

Writes a partial span, waiting only for host readiness and honouring transfer cancellation.

write

Writes all supplied bytes, with bounded host buffering.

finish

Finishes the only committed body.

Friends

Name Description

webcpp::wasi::http::write_task

Owns a nonblocking body write and stable pending trailer storage.

webcpp::wasi::http::body_writer::body_writer

Constructors

Synopses

Constructs an empty writer.

constexpr
body_writer() = default;

Transfers outgoing body ownership.

body_writer(body_writer&&) = default;

Takes ownership of a P2 outgoing body.

explicit
body_writer(wasi_http_types_own_outgoing_body_t body);

Parameters

Name Description

body

The outgoing body resource.

webcpp::wasi::http::body_writer::body_writer

Constructs an empty writer.

Synopsis

constexpr
body_writer() = default;

webcpp::wasi::http::body_writer::body_writer

Transfers outgoing body ownership.

Synopsis

body_writer(body_writer&&) = default;

webcpp::wasi::http::body_writer::body_writer

Takes ownership of a P2 outgoing body.

Synopsis

explicit
body_writer(wasi_http_types_own_outgoing_body_t body);

Parameters

Name Description

body

The outgoing body resource.

webcpp::wasi::http::body_writer::~body_writer

Fails and releases any unfinished outgoing body.

Synopsis

~body_writer();

webcpp::wasi::http::body_writer::operator=

Forbids replacing a potentially unfinished body.

Synopsis

body_writer&
operator=(body_writer&&) = delete;

webcpp::wasi::http::body_writer::write_some

Writes a partial span, waiting only for host readiness and honouring transfer cancellation.

Synopsis

std::size_t
write_some(
    std::string_view bytes,
    transfer_options const& options,
    message_error& error);

Return Value

The number of bytes transferred before completion or failure.

Parameters

Name Description

bytes

Source payload bytes.

options

Cancellation and deadline.

error

Receives any transfer failure.

webcpp::wasi::http::body_writer::write

Writes all supplied bytes, with bounded host buffering.

Synopsis

message_error
write(
    std::string_view bytes,
    transfer_options const& options = {});

Return Value

An empty error value on success, otherwise the failure.

Parameters

Name Description

bytes

Source payload.

options

Cancellation and deadline.

webcpp::wasi::http::body_writer::finish

Finishes the only committed body.

Synopsis

message_error
finish(message_fields* trailers = nullptr);

Return Value

An empty error value on success, otherwise the failure.

Parameters

Name Description

trailers

Optional fields whose resource is transferred to the host.

webcpp::wasi::http::incoming_request

An incoming host request.

Synopsis

class incoming_request;

Description

Its child body must be destroyed before this owner on P2.

Member Functions

Name Description

incoming_request [constructor]

Constructors

operator= [deleted]

Forbids copying resource ownership.

~incoming_request [destructor]

Releases metadata and any unconsumed request.

method

Borrows the method name.

target

Borrows the original path and query.

authority

Borrows the request authority.

headers

Acquires the exposed header fields.

consume

Consumes the content once, after reading metadata.

webcpp::wasi::http::incoming_request::incoming_request

Constructors

Synopses

Forbids copying resource ownership.

Takes ownership of the incoming request.

explicit
incoming_request(request_handle request);

Parameters

Name Description

request

Request supplied by the exported handler.

webcpp::wasi::http::incoming_request::incoming_request

Forbids copying resource ownership.

Synopsis

incoming_request(incoming_request const&) = delete;

webcpp::wasi::http::incoming_request::incoming_request

Takes ownership of the incoming request.

Synopsis

explicit
incoming_request(request_handle request);

Parameters

Name Description

request

Request supplied by the exported handler.

webcpp::wasi::http::incoming_request::operator=

Forbids copying resource ownership.

Synopsis

incoming_request&
operator=(incoming_request const&) = delete;

webcpp::wasi::http::incoming_request::~incoming_request

Releases metadata and any unconsumed request.

Synopsis

~incoming_request();

webcpp::wasi::http::incoming_request::method

Borrows the method name.

Synopsis

[[nodiscard]]
std::string_view
method() const;

Return Value

A view valid while this request owner remains alive.

Note

The return value should not be discarded.

webcpp::wasi::http::incoming_request::target

Borrows the original path and query.

Synopsis

[[nodiscard]]
std::string_view
target() const;

Return Value

A view valid while this request owner remains alive.

Note

The return value should not be discarded.

webcpp::wasi::http::incoming_request::authority

Borrows the request authority.

Synopsis

[[nodiscard]]
std::string_view
authority() const;

Return Value

A view valid while this request owner remains alive.

Note

The return value should not be discarded.

webcpp::wasi::http::incoming_request::headers

Acquires the exposed header fields.

Synopsis

[[nodiscard]]
message_fields
headers() const;

Return Value

Owning fields exposing the host headers.

Note

The return value should not be discarded.

webcpp::wasi::http::incoming_request::consume

Consumes the content once, after reading metadata.

Synopsis

body_reader
consume(message_error& error);

Description

Consumes the response or request body once.

Return Value

The body reader, or an empty reader on failure.

Parameters

Name Description

error

Receives an invalid-state error on repeated consumption.

webcpp::wasi::http::incoming_response

A response received from the host client, including its body resource lifetime.

Synopsis

class incoming_response;

Member Functions

Name Description

incoming_response [constructor]

Constructors

operator=

Replaces response ownership.

status

Returns the host response status code.

headers

Acquires the exposed header fields.

consume

Consumes the response or request body once.

webcpp::wasi::http::incoming_response::incoming_response

Constructors

Synopses

Constructs an empty response owner.

constexpr
incoming_response() = default;

Transfers response ownership.

Takes ownership of a P2 response.

explicit
incoming_response(wasi_http_types_own_incoming_response_t value);

Parameters

Name Description

value

Incoming host response.

webcpp::wasi::http::incoming_response::incoming_response

Constructs an empty response owner.

Synopsis

constexpr
incoming_response() = default;

webcpp::wasi::http::incoming_response::incoming_response

Transfers response ownership.

Synopsis

incoming_response(incoming_response&&) = default;

webcpp::wasi::http::incoming_response::incoming_response

Takes ownership of a P2 response.

Synopsis

explicit
incoming_response(wasi_http_types_own_incoming_response_t value);

Parameters

Name Description

value

Incoming host response.

webcpp::wasi::http::incoming_response::operator=

Replaces response ownership.

Synopsis

incoming_response&
operator=(incoming_response&&) = default;

Return Value

A reference to this object.

webcpp::wasi::http::incoming_response::status

Returns the host response status code.

Synopsis

[[nodiscard]]
uint16_t
status() const;

Return Value

The HTTP status code.

Note

The return value should not be discarded.

webcpp::wasi::http::incoming_response::headers

Acquires the exposed header fields.

Synopsis

[[nodiscard]]
message_fields
headers() const;

Return Value

Owning fields exposing the host headers.

Note

The return value should not be discarded.

webcpp::wasi::http::incoming_response::consume

Consumes the response or request body once.

Synopsis

body_reader
consume(message_error& error);

Return Value

The body reader, or an empty reader on failure.

Parameters

Name Description

error

Receives a resource-state failure.

webcpp::wasi::http::message_error

An operation failure, retaining the pinned wasi:http error-code discriminant.

Synopsis

struct message_error;

Member Functions

Name Description

operator bool

Whether this value represents failure.

Data Members

Name Description

code

Portable failure classification.

host_code

Host error-code discriminant when code is host_error.

webcpp::wasi::http::message_error::operator bool

Whether this value represents failure.

Synopsis

explicit
operator bool() const noexcept;

Return Value

Whether the object holds a resource or failure, as documented.

webcpp::wasi::http::message_error::code

Portable failure classification.

Synopsis

message_errc code = message_errc::success;

webcpp::wasi::http::message_error::host_code

Host error-code discriminant when code is host_error.

Synopsis

uint8_t host_code = 0;

webcpp::wasi::http::message_fields

Owning HTTP fields.

Synopsis

class message_fields;

Description

Duplicate values retain their order.

Member Functions

Name Description

message_fields [constructor]

Constructors

operator=

Replaces the field resource.

release

Transfers the resource to the component ABI.

append

Appends a field without silently dropping forbidden values.

for_each

Visits borrowed field strings, valid only during the call.

webcpp::wasi::http::message_fields::message_fields

Constructors

Synopses

Constructs empty mutable fields in the host.

Transfers the field resource.

Takes ownership of host fields.

explicit
message_fields(wasi_http_types_own_fields_t fields);

Parameters

Name Description

fields

The host resource to consume.

webcpp::wasi::http::message_fields::message_fields

Constructs empty mutable fields in the host.

Synopsis

message_fields();

webcpp::wasi::http::message_fields::message_fields

Transfers the field resource.

Synopsis

message_fields(message_fields&&) = default;

webcpp::wasi::http::message_fields::message_fields

Takes ownership of host fields.

Synopsis

explicit
message_fields(wasi_http_types_own_fields_t fields);

Parameters

Name Description

fields

The host resource to consume.

webcpp::wasi::http::message_fields::operator=

Replaces the field resource.

Synopsis

message_fields&
operator=(message_fields&&) = default;

Return Value

A reference to this object.

webcpp::wasi::http::message_fields::release

Transfers the resource to the component ABI.

Synopsis

auto
release();

Return Value

The resource handle, whose ownership passes to the caller.

webcpp::wasi::http::message_fields::append

Appends a field without silently dropping forbidden values.

Synopsis

message_error
append(
    std::string_view name,
    std::string_view value);

Return Value

An empty error value on success, otherwise the failure.

Parameters

Name Description

name

Field name.

value

Field value.

webcpp::wasi::http::message_fields::for_each

Visits borrowed field strings, valid only during the call.

Synopsis

template<class Visitor>
void
for_each(Visitor&& visitor) const;

Template Parameters

Name Description

Visitor

Callable accepting name and value string views.

Parameters

Name Description

visitor

Callback invoked for each field.

webcpp::wasi::http::outgoing_exchange

An outgoing exchange.

Synopsis

class outgoing_exchange;

Description

Destroying it cancels a pending response; send the body before receive.

Member Functions

Name Description

outgoing_exchange [constructor]

Constructors

operator= [deleted]

Forbids duplicate exchange ownership.

start

Starts an outgoing host request without guest DNS, TCP or TLS.

receive

Waits for response headers independently of its body.

Friends

Name Description

webcpp::wasi::http::response_task

Polls an outgoing exchange’s response without waiting for network readiness.

webcpp::wasi::http::outgoing_exchange::outgoing_exchange

Constructors

Synopses

Constructs an idle exchange.

constexpr
outgoing_exchange() = default;

Forbids duplicate exchange ownership.

webcpp::wasi::http::outgoing_exchange::outgoing_exchange

Constructs an idle exchange.

Synopsis

constexpr
outgoing_exchange() = default;

webcpp::wasi::http::outgoing_exchange::outgoing_exchange

Forbids duplicate exchange ownership.

Synopsis

outgoing_exchange(outgoing_exchange const&) = delete;

webcpp::wasi::http::outgoing_exchange::operator=

Forbids duplicate exchange ownership.

Synopsis

outgoing_exchange&
operator=(outgoing_exchange const&) = delete;

webcpp::wasi::http::outgoing_exchange::start

Starts an outgoing host request without guest DNS, TCP or TLS.

Synopsis

body_writer
start(
    std::string_view scheme,
    std::string_view authority,
    std::string_view method,
    std::string_view target,
    message_fields headers,
    message_error& error);

Return Value

The committed body writer, or an empty writer on failure.

Parameters

Name Description

scheme

HTTP or HTTPS.

authority

Host and optional port.

method

HTTP method name.

target

Path and query.

headers

Fields transferred to the host.

error

Receives any request construction or host failure.

webcpp::wasi::http::outgoing_exchange::receive

Waits for response headers independently of its body.

Synopsis

incoming_response
receive(
    transfer_options const& options,
    message_error& error);

Return Value

The response owner, or an empty owner on failure.

Parameters

Name Description

options

Cancellation and deadline.

error

Receives any host or cancellation failure.

webcpp::wasi::http::read_task

Owns a nonblocking body read and its pending completion resources.

Synopsis

class read_task;

Description

A pending read borrows the same destination span until a later poll completes. Destroying this object cancels pending host access before releasing resources.

Member Functions

Name Description

read_task [constructor]

Constructors

~read_task [destructor]

Cancels pending host operations before releasing their borrowed memory.

eof

Reports whether body content ended; finish still resolves trailers.

read

Polls one partial read without blocking for data.

finish

Polls trailers and consumption completion after EOF.

webcpp::wasi::http::read_task::read_task

Constructors

Synopses

Forbids moving a task with host-borrowed result storage.

read_task(read_task&&) = delete;

Takes ownership of an incoming body.

explicit
read_task(body_reader body);

Parameters

Name Description

body

Incoming body owner.

webcpp::wasi::http::read_task::read_task

Forbids moving a task with host-borrowed result storage.

Synopsis

read_task(read_task&&) = delete;

webcpp::wasi::http::read_task::read_task

Takes ownership of an incoming body.

Synopsis

explicit
read_task(body_reader body);

Parameters

Name Description

body

Incoming body owner.

webcpp::wasi::http::read_task::~read_task

Cancels pending host operations before releasing their borrowed memory.

Synopsis

~read_task();

webcpp::wasi::http::read_task::eof

Reports whether body content ended; finish still resolves trailers.

Synopsis

[[nodiscard]]
bool
eof() const noexcept;

Return Value

Whether input reached EOF.

Note

The return value should not be discarded.

webcpp::wasi::http::read_task::read

Polls one partial read without blocking for data.

Synopsis

bool
read(
    std::span<char> buffer,
    std::size_t& count,
    transfer_options const& options,
    message_error& error);

Return Value

False while pending, true when bytes, EOF or failure are available.

Parameters

Name Description

buffer

Stable destination while a read is pending.

count

Receives the completed byte count.

options

Incoming limit, cancellation and deadline.

error

Receives any completed failure.

webcpp::wasi::http::read_task::finish

Polls trailers and consumption completion after EOF.

Synopsis

template<class Visitor>
bool
finish(
    Visitor&& visitor,
    transfer_options const& options,
    message_error& error);

Return Value

False while pending, true on completion or failure.

Template Parameters

Name Description

Visitor

Callable accepting trailer name and value views.

Parameters

Name Description

visitor

Callback for complete trailer fields.

options

Cancellation and deadline.

error

Receives any completed failure.

webcpp::wasi::http::response

What the program’s main answers one HTTP request with: a status, a content type and a body.

Synopsis

struct response;

Description

The handler sends the status, a content-type header when the content type is not empty, and the body; it sends no other header.

Note

An aggregate whose every member has a default, so that a designated initializer names only what differs: {.status = 404}.

Data Members

Name Description

status

The status code, 200 unless set.

content_type

The value of the content-type header, which is sent only when it is not empty.

body

The body, sent whole, however long.

See Also

"The response", in the guide.

webcpp::wasi::http::response::status

The status code, 200 unless set.

Synopsis

unsigned int status = 200;

Description

It is sent as it is when it lies within 100 to 599, the five classes of HTTP. Outside them the handler sends no response of the main’s and answers an internal error instead, which wasmtime 50.0.1 sends as its own 500 Internal Server Error.

webcpp::wasi::http::response::content_type

The value of the content-type header, which is sent only when it is not empty.

Synopsis

std::string_view content_type = {};

Description

A value the host refuses as a field value, one that holds a CR, an LF or a NUL for example, is not dropped: the handler answers an internal error instead of the response.

Note

It is read after the main has returned, so it views what outlives the main: a string literal, such as "application/json", or the method or the target the main was given, which the handler frees only once the response is written; never a string of the main’s own. A view costs no allocation, where a std::string of 16 characters exceeds the small-string buffer of wasm32’s libc++.

webcpp::wasi::http::response::body

The body, sent whole, however long.

Synopsis

std::string body = {};

webcpp::wasi::http::response_task

Polls an outgoing exchange’s response without waiting for network readiness.

Synopsis

class response_task;

Member Functions

Name Description

response_task [constructor]

Borrows a started exchange until response completion.

poll

Polls response headers once.

webcpp::wasi::http::response_task::response_task

Borrows a started exchange until response completion.

Synopsis

explicit
response_task(outgoing_exchange& exchange);

Parameters

Name Description

exchange

Started outgoing host exchange.

webcpp::wasi::http::response_task::poll

Polls response headers once.

Synopsis

bool
poll(
    incoming_response& response,
    transfer_options const& options,
    message_error& error);

Return Value

False while pending, true on response or failure.

Parameters

Name Description

response

Receives ownership of the completed response.

options

Cancellation and deadline.

error

Receives any completed failure.

webcpp::wasi::http::transfer_options

Limits shared by a transfer.

Synopsis

struct transfer_options;

Description

Cancellation is checked while waiting and between chunks.

Member Functions

Name Description

expires_after

Sets an absolute monotonic deadline.

check

Checks the cancellation source and the absolute deadline.

Data Members

Name Description

body_limit

Maximum incoming payload size in bytes.

deadline

Absolute host monotonic deadline, or the maximum value for none.

cancelled

Optional cancellation predicate, called on the component task.

cancellation_context

Opaque state supplied to the cancellation predicate.

webcpp::wasi::http::transfer_options::expires_after

Sets an absolute monotonic deadline.

Synopsis

void
expires_after(uint64_t nanoseconds);

Parameters

Name Description

nanoseconds

Duration from the current host monotonic time.

webcpp::wasi::http::transfer_options::check

Checks the cancellation source and the absolute deadline.

Synopsis

[[nodiscard]]
message_error
check() const;

Return Value

An empty error value on success, otherwise the failure.

Note

The return value should not be discarded.

webcpp::wasi::http::transfer_options::body_limit

Maximum incoming payload size in bytes.

Synopsis

uint64_t body_limit = static_cast<long long>(static_cast<std::uint64_t>(1024) * 1024);

webcpp::wasi::http::transfer_options::deadline

Absolute host monotonic deadline, or the maximum value for none.

Synopsis

uint64_t deadline = std::numeric_limits<std::uint64_t>::max();

webcpp::wasi::http::transfer_options::cancelled

Optional cancellation predicate, called on the component task.

Synopsis

bool(* cancelled)(void*) = nullptr;

webcpp::wasi::http::transfer_options::cancellation_context

Opaque state supplied to the cancellation predicate.

Synopsis

void* cancellation_context = nullptr;

webcpp::wasi::http::write_task

Owns a nonblocking body write and stable pending trailer storage.

Synopsis

class write_task;

Member Functions

Name Description

write_task [constructor]

Constructors

~write_task [destructor]

Cancels pending writes before releasing borrowed payload storage.

write

Polls a bounded partial write without waiting for readiness.

finish

Polls the final trailer write without committing another response.

webcpp::wasi::http::write_task::write_task

Constructors

Synopses

Forbids moving a task with host-borrowed result storage.

write_task(write_task&&) = delete;

Takes ownership of the only outgoing body.

explicit
write_task(body_writer body);

Parameters

Name Description

body

Outgoing body owner.

webcpp::wasi::http::write_task::write_task

Forbids moving a task with host-borrowed result storage.

Synopsis

write_task(write_task&&) = delete;

webcpp::wasi::http::write_task::write_task

Takes ownership of the only outgoing body.

Synopsis

explicit
write_task(body_writer body);

Parameters

Name Description

body

Outgoing body owner.

webcpp::wasi::http::write_task::~write_task

Cancels pending writes before releasing borrowed payload storage.

Synopsis

~write_task();

webcpp::wasi::http::write_task::write

Polls a bounded partial write without waiting for readiness.

Synopsis

bool
write(
    std::string_view bytes,
    std::size_t& count,
    transfer_options const& options,
    message_error& error);

Return Value

False while pending, true when bytes or failure are available.

Parameters

Name Description

bytes

Stable source until the pending write completes.

count

Receives the completed byte count.

options

Cancellation and deadline.

error

Receives any completed failure.

webcpp::wasi::http::write_task::finish

Polls the final trailer write without committing another response.

Synopsis

bool
finish(
    message_fields* trailers,
    message_error& error);

Return Value

False while pending, true on completion or failure.

Parameters

Name Description

trailers

Optional fields consumed when finishing begins.

error

Receives any completed failure.

webcpp::wasi::http::message_errc

Errors of a host message operation, independent of a C++ exception runtime.

Synopsis

enum class message_errc : uint8_t;

Members

Name Description

success

No failure.

invalid_message

Invalid HTTP metadata.

forbidden_header

A field forbidden by the host.

body_limit

The incoming body exceeded its configured limit.

cancelled

The caller cancelled the transfer.

timed_out

The transfer deadline expired.

closed

A stream closed before the operation completed.

host_error

A pinned wasi:http error-code.

invalid_state

The resource has already been consumed or finished.

webcpp::wasi::http::commit_response

Commits response headers once and returns its body writer.

Synopsis

body_writer
commit_response(
    response_out out,
    uint16_t status,
    message_fields headers,
    message_error& error);

Return Value

The committed body writer, or an empty writer on failure.

Parameters

Name Description

out

Active response commitment resource.

status

HTTP status code.

headers

Header fields transferred to the host.

error

Receives any pre-commit failure.

webcpp::wasi::http::http_main

Answers one HTTP request, given its method and its target: the program’s main.

Synopsis

[[nodiscard]]
response
http_main(
    std::string_view method,
    std::string_view target);

Description

A program defines it once, by writing its body between WEBCPP_WASI_HTTP_MAIN_BEGIN and WEBCPP_WASI_HTTP_MAIN_END; the handler calls it once for each request and sends what it returns. The body is inside a function of the namespace webcpp::wasi::http, so it names the program’s own code with its full namespace, or with a namespace alias declared at its top.

Note

Both parameters view the request, which the handler frees once it has written the response: the response’s content type may view them, and the main copies what it keeps beyond the request.

Note

The handler catches nothing. On wasip2 and wasip3, where webcpp builds a program with exceptions, an exception that escapes the main traps the instance: wasmtime 50.0.1 prints the trap and answers the client with its own 500 Internal Server Error.

Return Value

The response the handler sends.

Note

The return value should not be discarded.

Parameters

Name Description

method

The request’s method as it travels on the wire, such as "GET", or an extension method as it was sent.

target

The request target, its path and query, such as "/v1/greeting?name=ana", or "/" when the request carried none.

See Also

"Your main", in the guide.

webcpp::wasi::http::refuse_response

Reports a pre-commit error.

Synopsis

void
refuse_response(response_out out);

Description

Never call after committing.

Parameters

Name Description

out

Active response commitment resource.

webcpp::wasi::stdio_channel

Owns standard WASI CLI streams.

Synopsis

Declared in <webcpp/wasi/stdio.hpp>

class stdio_channel;

Description

Readiness polling waits are bounded to ten milliseconds.

Member Functions

Name Description

stdio_channel [constructor]

Acquires the standard input and output streams.

~stdio_channel [destructor]

Releases all owned CLI stream resources.

read

Reads a partial byte span.

write

Writes a partial byte span.

webcpp::wasi::stdio_channel::stdio_channel

Acquires the standard input and output streams.

Synopsis

Declared in <webcpp/wasi/stdio.hpp>

stdio_channel();

webcpp::wasi::stdio_channel::~stdio_channel

Releases all owned CLI stream resources.

Synopsis

Declared in <webcpp/wasi/stdio.hpp>

~stdio_channel();

webcpp::wasi::stdio_channel::read

Reads a partial byte span.

Synopsis

Declared in <webcpp/wasi/stdio.hpp>

stream_result
read(std::span<char> buffer);

Return Value

The partial byte count and readiness or failure status.

Parameters

Name Description

buffer

Destination bytes.

webcpp::wasi::stdio_channel::write

Writes a partial byte span.

Synopsis

Declared in <webcpp/wasi/stdio.hpp>

[[nodiscard]]
stream_result
write(std::span<char const> buffer) const;

Return Value

The partial byte count and readiness or failure status.

Note

The return value should not be discarded.

Parameters

Name Description

buffer

Source bytes.

webcpp::wasi::stream_result

Bytes copied by a partial stream operation, independent of any wire protocol.

Synopsis

Declared in <webcpp/wasi/stdio.hpp>

struct stream_result;

Data Members

Name Description

count

Bytes copied during this operation.

status

Transfer status, distinct from the byte count.

webcpp::wasi::stream_result::count

Bytes copied during this operation.

Synopsis

Declared in <webcpp/wasi/stdio.hpp>

std::size_t count = 0;

webcpp::wasi::stream_result::status

Transfer status, distinct from the byte count.

Synopsis

Declared in <webcpp/wasi/stdio.hpp>

stream_status status = stream_status::ready;

webcpp::wasi::stream_status

The observable result of a partial host stream operation.

Synopsis

Declared in <webcpp/wasi/stdio.hpp>

enum class stream_status : int;

Members

Name Description

ready

Bytes transferred successfully.

blocked

No progress before the bounded wait ended.

eof

Input ended.

failed

The host reported an I/O failure.

WEBCPP_WASI_HTTP_EXPORTS

Defines the extern "C" handler the target’s world exports, which hands each request to the library’s handler.

Synopsis

#define WEBCPP_WASI_HTTP_EXPORTS()

Description

On wasip2 that is the handler of wasi:http/incoming-handler; on wasip3, the handler of wasi:http/handler and its callback. WEBCPP_WASI_HTTP_MAIN_BEGIN expands it, so a program that writes its main between the two macros never names it.

Note

The functions it defines have the names wit-bindgen generates for each world: exports_wasi_http_incoming_handler_handle on wasip2, and exports_wasi_http_handler_handle with its exports_wasi_http_handler_handle_callback on wasip3. The component’s glue, webcpp_wasi_http.c, calls them by those names.

See Also

"How a request flows", in the guide.

WEBCPP_WASI_HTTP_MAIN_BEGIN

Opens the definition of the program’s main, after the handler the target’s world exports.

Synopsis

#define WEBCPP_WASI_HTTP_MAIN_BEGIN(method, target)

Description

The main’s body follows it, returns a webcpp::wasi::http::response for the request, and ends with WEBCPP_WASI_HTTP_MAIN_END. Together they define webcpp::wasi::http::http_main and, before it, the extern "C" handler of WEBCPP_WASI_HTTP_EXPORTS.

Note

Write the pair once in a program, in one source file: a program has one main and exports one handler.

Parameters

Name Description

method

The name the body gives the request’s method, a std::string_view such as "GET".

target

The name the body gives the request target, its path and query, a std::string_view such as "/v1/greeting?name=ana".

See Also

"Your main", in the guide.

WEBCPP_WASI_HTTP_MAIN_END

Closes the definition of the program’s main that WEBCPP_WASI_HTTP_MAIN_BEGIN opened.

Synopsis

#define WEBCPP_WASI_HTTP_MAIN_END()

See Also

"Your main", in the guide.

WEBCPP_WASI_NO_EXCEPTIONS

Defined when wasi is built without exceptions: automatically when the compiler has none (where Boost.Config defines BOOST_NO_EXCEPTIONS), when the developer defines WEBCPP_NO_EXCEPTIONS for every webcpp library, or when the developer defines it alone, to disable wasi’s in a build that has them.

Synopsis

Declared in <webcpp/wasi/config.hpp>

#define WEBCPP_WASI_NO_EXCEPTIONS

Description

wasi raises no exception of its own, and its handler catches none, so the macro changes nothing in it. It is provided so that a program can set it for every webcpp library alike. An exception wasi raised would go through boost::throw_exception, and an API that throws would be absent while this is defined.

See Also

"Exceptions on wasip2 and wasip3", in the guide.

Appendix A: Changelog

What changed in wasi in each version of webcpp, newest first, one line per change. "Unreleased" is what the next version brings. The release notes of every version are on webcpp’s index page.

Unreleased

webcpp 0.1.0

  • Updated Wasmtime instructions to 50.0.1, preserving historical measurements.

  • Fixed a status outside 100-599 answering 200 OK; it now answers an internal error.

  • Fixed a content type viewing the request’s method or target not being sent.

  • Fixed a content type the host refused being dropped silently; it now answers an internal error.

  • Added the WEBCPP_WASI_NO_EXCEPTIONS configuration macro, defined automatically or by the developer.

  • Updated the documentation: wasmtime’s versions, the by-hand build, escaping exceptions and a changelog.

  • Added WEBCPP_NO_EXCEPTIONS, which disables wasi’s exceptions with every webcpp library’s.