---
# System prepended metadata

title: C@E ABI needs

---

# C@E ABI needs

Goal here is to review the current ABI and:

* Ensure we have a path forward for a parallel interface-type / component-based version.
* Uncover feature dependencies, i.e. nail down the "shortest path" to introducing a parallel ABI.

## A bit of context

In the previous discussion on this topic, we found that there are many moving pieces around the component model, but that it would be very worthwhile to identify a kind of "component model MVP" for C@E. This MVP would aim to:

* Specify a parallel ABI 

---

# Concerns

* Resources do not exist yet
* `select`-style API: do we do subtyping, or require a single namespace of resource IDs and give you access to them
* `read` requires guest allocation each time (can't provide the host a buffer to write into)
    * that prevents doing a `read` syscall directly into the guest memory
    * also means that if you're doing a `read` loop you alloc and dealloc each time around the loop :(
    * we are tying our hands behind our back here with interface types; long-term will address through streams or similar
        * it's maybe possible to expose this if absolutely needed, but will be complex to break through layers of abstraction
* Ownership for resources
    * Is handle re-use a thing?
    * Probably `Rc`-like semantics on the host side? Not sure how we're going to represent.
        * This generates a host of problems ... 
    * Or possibly we apply a Rust-like ownership system as part of interface types?
    * Lots of question marks here.
* Hostcall extensibility -- allowing configuration to grow, backwards-compatibly, by introducing optional fields/arguments
    * Subtyping could be part of the answer, but we've thought about that specifically between components; here we need it for the host
    * Could introduce shim components that translate between versions of hostcalls using import/export wrappers
        * Unclear where exactly this wrapping happens, how it's specfied (does it fall out of subtyping?) etc

# Type names

```
;;; Status codes returned from hostcalls.
(typename $fastly_status
    (enum (@witx tag u32)
        ;;; Success value.
        ;;
        ;;; This indicates that a hostcall finished successfully.
        $ok
        ;;; Generic error value.
        ;;
        ;;; This means that some unexpected error occured during a hostcall.
        $error
        ;;; Invalid argument.
        $inval
        ;;; Invalid handle.
        ;;
        ;;; Thrown when a request, response, or body handle is not valid.
        $badf
        ;;; Buffer length error.
        ;;
        ;;; Thrown when a buffer is too long.
        $buflen
        ;;; Unsupported operation error.
        ;;
        ;;; This error is thrown when some operation cannot be performed, because it is not supported.
        $unsupported
        ;;; Alignment error.
        ;;
        ;;; This is thrown when a pointer does not point to a properly aligned slice of memory.
        $badalign
        ;;; Invalid HTTP error.
        ;;
        ;;; This can be thrown when a method, URI, header, or status is not valid. This can also
        ;;; be thrown if a message head is too large.
        $httpinvalid
        ;;; HTTP user error.
        ;;
        ;;; This is thrown in cases where user code caused an HTTP error. For example, attempt to send
        ;;; a 1xx response code, or a request with a non-absolute URI. This can also be caused by
        ;;; an unexpected header: both `content-length` and `transfer-encoding`, for example.
        $httpuser
        ;;; HTTP incomplete message error.
        ;;
        ;;; This can be thrown when a stream ended unexpectedly.
        $httpincomplete
        ;;; A `None` error.
        ;;
        ;;; This status code is used to indicate when an optional value did not exist, as opposed to
        ;;; an empty value.
        $none
        ;;; Message head too large.
        $httpheadtoolarge
        ;;; Invalid HTTP status.
        $httpinvalidstatus
    )
)

;;; A tag indicating HTTP protocol versions.
(typename $http_version
    (enum (@witx tag u32)
        $http_09
        $http_10
        $http_11
        $h2
        $h3
    )
)

;;; HTTP status codes.
(typename $http_status u16)

(typename $body_write_end
    (enum (@witx tag u32)
        $back
        $front
    )
)

;;; A handle to an HTTP request or response body.
(typename $body_handle (handle))
;;; A handle to an HTTP request.
(typename $request_handle (handle))
;;; A handle to an HTTP response.
(typename $response_handle (handle))
;;; A handle to a currently-pending asynchronous HTTP request.
(typename $pending_request_handle (handle))
;;; A handle to a logging endpoint.
(typename $endpoint_handle (handle))
;;; A handle to an Edge Dictionary.
(typename $dictionary_handle (handle))

;;; A "multi-value" cursor.
(typename $multi_value_cursor u32)
;;; -1 represents "finished", non-negative represents a $multi_value_cursor:
(typename $multi_value_cursor_result s64)

;;; An override for response caching behavior.
(typename $cache_override_tag
    (flags (@witx repr u32)
        ;;; Do not override the behavior specified in the origin response's cache control headers.
        $none
        ;;; Do not cache the response to this request, regardless of the origin response's headers.
        $pass
        $ttl
        $stale_while_revalidate
        $pci
    )
)
(typename $num_bytes (@witx usize))
(typename $header_count u32)
(typename $is_done u32)
(typename $done_idx u32)

(typename $content_encodings
    (flags (@witx repr u32)
        $gzip))

(typename $framing_headers_mode
    (enum (@witx tag u32)
        $automatic
        $manually_from_headers))

(typename $tls_version
    (flags (@witx repr u32)
       $tls_1
       $tls_1_1
       $tls_1_2
       $tls_1_3))

(typename $backend_config_options
    (flags (@witx repr u32)
       $reserved
       $host_override
       $connect_timeout
       $first_byte_timeout
       $between_bytes_timeout
       $use_ssl
       $ssl_min_version
       $ssl_max_version
       $cert_hostname
       $ca_cert
       $ciphers
       $sni_hostname))

(typename $dynamic_backend_config
  (record
    (field $host_override (@witx pointer (@witx char8)))
    (field $host_override_len u32)
    (field $connect_timeout_ms u32)
    (field $first_byte_timeout_ms u32)
    (field $between_bytes_timeout_ms u32)
    (field $ssl_min_version $tls_version)
    (field $ssl_max_version $tls_version)
    (field $cert_hostname (@witx pointer (@witx char8)))
    (field $cert_hostname_len u32)
    (field $ca_cert (@witx pointer (@witx char8)))
    (field $ca_cert_len u32)
    (field $ciphers (@witx pointer (@witx char8)))
    (field $ciphers_len u32)
    (field $sni_hostname (@witx pointer (@witx char8)))
    (field $sni_hostname_len u32)
    ))
```

# Witx

```
(use "typenames.witx")

(module $fastly_abi
    (@interface func (export "init")
        (param $abi_version u64)
        (result $err (expected (error $fastly_status)))
    )
)

(module $fastly_uap
    (@interface func (export "parse")
        (param $user_agent string)

        (param $family (@witx pointer (@witx char8)))
        (param $family_len (@witx usize))
        (param $family_nwritten_out (@witx pointer (@witx usize)))

        (param $major (@witx pointer (@witx char8)))
        (param $major_len (@witx usize))
        (param $major_nwritten_out (@witx pointer (@witx usize)))

        (param $minor (@witx pointer (@witx char8)))
        (param $minor_len (@witx usize))
        (param $minor_nwritten_out (@witx pointer (@witx usize)))

        (param $patch (@witx pointer (@witx char8)))
        (param $patch_len (@witx usize))
        (param $patch_nwritten_out (@witx pointer (@witx usize)))

        (result $err (expected (error $fastly_status)))
    )
)

(module $fastly_http_body
    (@interface func (export "append")
        (param $dest $body_handle)
        (param $src $body_handle)
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "new")
        (result $err (expected $body_handle (error $fastly_status)))
    )

    (@interface func (export "read")
        (param $h $body_handle)
        (param $buf (@witx pointer u8))
        (param $buf_len (@witx usize))
        (result $err (expected $num_bytes (error $fastly_status)))
    )

    (@interface func (export "write")
        (param $h $body_handle)
        (param $buf (list u8))
        (param $end $body_write_end)
        (result $err (expected $num_bytes (error $fastly_status)))
    )

    (@interface func (export "close")
        (param $h $body_handle)
        (result $err (expected (error $fastly_status)))
    )
)

(module $fastly_log
    (@interface func (export "endpoint_get")
        (param $name (list u8))
        (result $err (expected $endpoint_handle (error $fastly_status)))
    )

    (@interface func (export "write")
        (param $h $endpoint_handle)
        (param $msg (list u8))
        (result $err (expected $num_bytes (error $fastly_status)))
    )
)

(module $fastly_http_req
    (@interface func (export "body_downstream_get")
        (result $err (expected
                (tuple $request_handle $body_handle)
                (error $fastly_status))
        )
    )

    (@interface func (export "cache_override_set")
        (param $h $request_handle)
        (param $tag $cache_override_tag)
        (param $ttl u32)
        (param $stale_while_revalidate u32)
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "cache_override_v2_set")
        (param $h $request_handle)
        (param $tag $cache_override_tag)
        (param $ttl u32)
        (param $stale_while_revalidate u32)
        (param $sk (list u8))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "downstream_client_ip_addr")
        ;;; must be a 16-byte array
        (param $addr_octets_out (@witx pointer (@witx char8)))
        (result $err (expected $num_bytes (error $fastly_status)))
    )

    (@interface func (export "downstream_tls_cipher_openssl_name")
        (param $cipher_out (@witx pointer (@witx char8)))
        (param $cipher_max_len (@witx usize))
        (param $nwritten_out (@witx pointer (@witx usize)))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "downstream_tls_protocol")
        (param $protocol_out (@witx pointer (@witx char8)))
        (param $protocol_max_len (@witx usize))
        (param $nwritten_out (@witx pointer (@witx usize)))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "downstream_tls_client_hello")
        (param $chello_out (@witx pointer (@witx char8)))
        (param $chello_max_len (@witx usize))
        (param $nwritten_out (@witx pointer (@witx usize)))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "downstream_tls_ja3_md5")
        ;; must be a 16-byte array
        (param $cja3_md5_out (@witx pointer (@witx char8)))
        (result $err (expected $num_bytes (error $fastly_status)))
    )

    (@interface func (export "new")
        (result $err (expected $request_handle (error $fastly_status)))
    )

    (@interface func (export "header_names_get")
        (param $h $request_handle)
        (param $buf (@witx pointer (@witx char8)))
        (param $buf_len (@witx usize))
        (param $cursor $multi_value_cursor)
        (param $ending_cursor_out (@witx pointer $multi_value_cursor_result))
        (param $nwritten_out (@witx pointer (@witx usize)))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "original_header_names_get")
        (param $buf (@witx pointer (@witx char8)))
        (param $buf_len (@witx usize))
        (param $cursor $multi_value_cursor)
        (param $ending_cursor_out (@witx pointer $multi_value_cursor_result))
        (param $nwritten_out (@witx pointer (@witx usize)))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "original_header_count")
        (result $err (expected $header_count (error $fastly_status)))
    )

    (@interface func (export "header_value_get")
        (param $h $request_handle)
        (param $name (list u8))
        (param $value (@witx pointer (@witx char8)))
        (param $value_max_len (@witx usize))
        (param $nwritten_out (@witx pointer (@witx usize)))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "header_values_get")
        (param $h $request_handle)
        (param $name (list u8))
        (param $buf (@witx pointer (@witx char8)))
        (param $buf_len (@witx usize))
        (param $cursor $multi_value_cursor)
        (param $ending_cursor_out (@witx pointer $multi_value_cursor_result))
        (param $nwritten_out (@witx pointer (@witx usize)))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "header_values_set")
        (param $h $request_handle)
        (param $name (list u8))
        ;;; contains multiple values separated by \0
        (param $values (list (@witx char8)))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "header_insert")
        (param $h $request_handle)
        (param $name (list u8))
        (param $value (list u8))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "header_append")
        (param $h $request_handle)
        (param $name (list u8))
        (param $value (list u8))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "header_remove")
        (param $h $request_handle)
        (param $name (list u8))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "method_get")
        (param $h $request_handle)
        (param $buf (@witx pointer (@witx char8)))
        (param $buf_len (@witx usize))
        (param $nwritten_out (@witx pointer (@witx usize)))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "method_set")
        (param $h $request_handle)
        (param $method string)
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "uri_get")
        (param $h $request_handle)
        (param $buf (@witx pointer (@witx char8)))
        (param $buf_len (@witx usize))
        (param $nwritten_out (@witx pointer (@witx usize)))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "uri_set")
        (param $h $request_handle)
        (param $uri string)
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "version_get")
        (param $h $request_handle)
        (result $err (expected $http_version (error $fastly_status)))
    )

    (@interface func (export "version_set")
        (param $h $request_handle)
        (param $version $http_version)
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "send")
        (param $h $request_handle)
        (param $b $body_handle)
        (param $backend string)
        (result $err (expected
                (tuple $response_handle $body_handle)
                (error $fastly_status))
        )
    )

    (@interface func (export "send_async")
        (param $h $request_handle)
        (param $b $body_handle)
        (param $backend string)
        (result $err (expected $pending_request_handle
                (error $fastly_status))
        )
    )

    (@interface func (export "send_async_streaming")
        (param $h $request_handle)
        (param $b $body_handle)
        (param $backend string)
        (result $err (expected $pending_request_handle (error $fastly_status)))
    )

    (@interface func (export "pending_req_poll")
        (param $h $pending_request_handle)
        (result $err (expected
                (tuple $is_done
                    $response_handle
                    $body_handle)
                (error $fastly_status))
        )
    )

    (@interface func (export "pending_req_wait")
        (param $h $pending_request_handle)
        (result $err (expected
                (tuple $response_handle $body_handle)
                (error $fastly_status))
        )
    )

    (@interface func (export "pending_req_select")
        (param $hs (list $pending_request_handle))
        (result $err (expected
                (tuple $done_idx $response_handle $body_handle)
                (error $fastly_status))
        )
    )

    (@interface func(export "close")
        (param $h $request_handle)
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "auto_decompress_response_set")
        (param $h $request_handle)
        (param $encodings $content_encodings)
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "upgrade_websocket")
        (param $backend_name string)
        (result $err (expected (error $fastly_status)))
    )

    ;;; Adjust how this requests's framing headers are determined.
    (@interface func (export "framing_headers_mode_set")
        (param $h $request_handle)
        (param $mode $framing_headers_mode)
        (result $err (expected (error $fastly_status)))
    )

    ;;; Create a backend for later use
    (@interface func (export "register_dynamic_backend")
        (param $name_prefix string)
        (param $target string)
        (param $backend_config_mask $backend_config_options)
        (param $backend_configuration (@witx pointer $dynamic_backend_config))
        (result $err (expected (error $fastly_status)))
    )
)

(module $fastly_http_resp
    (@interface func (export "new")
        (result $err (expected $response_handle (error $fastly_status)))
    )

    ;; The following directly mirror header & version methods on req

    (@interface func (export "header_names_get")
        (param $h $response_handle)
        (param $buf (@witx pointer (@witx char8)))
        (param $buf_len (@witx usize))
        (param $cursor $multi_value_cursor)
        (param $ending_cursor_out (@witx pointer $multi_value_cursor_result))
        (param $nwritten_out (@witx pointer (@witx usize)))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "header_value_get")
        (param $h $response_handle)
        (param $name (list u8))
        (param $value (@witx pointer (@witx char8)))
        (param $value_max_len (@witx usize))
        (param $nwritten_out (@witx pointer (@witx usize)))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "header_values_get")
        (param $h $response_handle)
        (param $name (list u8))
        (param $buf (@witx pointer (@witx char8)))
        (param $buf_len (@witx usize))
        (param $cursor $multi_value_cursor)
        (param $ending_cursor_out (@witx pointer $multi_value_cursor_result))
        (param $nwritten_out (@witx pointer (@witx usize)))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "header_values_set")
        (param $h $response_handle)
        (param $name (list u8))
        ;;; contains multiple values separated by \0
        (param $values (list (@witx char8)))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "header_insert")
        (param $h $response_handle)
        (param $name (list u8))
        (param $value (list u8))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "header_append")
        (param $h $response_handle)
        (param $name (list u8))
        (param $value (list u8))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "header_remove")
        (param $h $response_handle)
        (param $name (list u8))
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "version_get")
        (param $h $response_handle)
        (result $err (expected $http_version (error $fastly_status)))
    )

    (@interface func (export "version_set")
        (param $h $response_handle)
        (param $version $http_version)
        (result $err (expected (error $fastly_status)))
    )
    ;; End directly mirror header & version methods on req

    (@interface func (export "send_downstream")
        (param $h $response_handle)
        (param $b $body_handle)
        (param $streaming u32)
        (result $err (expected (error $fastly_status)))
    )

    (@interface func (export "status_get")
        (param $h $response_handle)
        (result $err (expected $http_status (error $fastly_status)))
    )

    (@interface func (export "status_set")
        (param $h $response_handle)
        (param $status $http_status)
        (result $err (expected (error $fastly_status)))
    )

    (@interface func(export "close")
        (param $h $response_handle)
        (result $err (expected (error $fastly_status)))
    )

    ;;; Adjust how this response's framing headers are determined.
    (@interface func (export "framing_headers_mode_set")
        (param $h $response_handle)
        (param $mode $framing_headers_mode)
        (result $err (expected (error $fastly_status)))
    )
)

(module $fastly_dictionary
    (@interface func (export "open")
        (param $name string)
        (result $err (expected $dictionary_handle (error $fastly_status)))
    )

    (@interface func (export "get")
        (param $h $dictionary_handle)
        (param $key string)
        (param $value (@witx pointer (@witx char8)))
        (param $value_max_len (@witx usize))
        (result $err (expected $num_bytes (error $fastly_status)))
    )
)

(module $fastly_geo
    (@interface func (export "lookup")
        (param $addr_octets (@witx const_pointer (@witx char8)))
        (param $addr_len (@witx usize))
        (param $buf (@witx pointer (@witx char8)))
        (param $buf_len (@witx usize))
        (param $nwritten_out (@witx pointer (@witx usize)))
        (result $err (expected (error $fastly_status)))
    )
)
```