diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ef8bcefda4..9ffd9f76f8 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -17,6 +17,7 @@ jobs: - test - features - ffi + - ffi-header - doc steps: - run: exit 0 @@ -119,9 +120,7 @@ jobs: ffi: name: Test C API (FFI) needs: [style] - runs-on: ubuntu-latest - steps: - name: Checkout uses: actions/checkout@v1 @@ -147,10 +146,6 @@ jobs: command: build args: --features client,http1,http2,ffi - # TODO: re-enable check once figuring out how to get it working in CI - # - name: Verify cbindgen - # run: ./capi/gen_header.sh --verify - - name: Make Examples run: cd capi/examples && make client @@ -162,6 +157,39 @@ jobs: command: test args: --features full,ffi --lib + ffi-header: + name: Verify hyper.h is up to date + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v1 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + default: true + override: true + components: cargo + + - name: Install cbindgen + uses: actions-rs/cargo@v1 + with: + command: install + args: cbindgen + + - name: Build FFI + uses: actions-rs/cargo@v1 + env: + RUSTFLAGS: --cfg hyper_unstable_ffi + with: + command: build + args: --features client,http1,http2,ffi + + - name: Ensure that hyper.h is up to date + run: ./capi/gen_header.sh --verify + doc: name: Build docs needs: [style, test] diff --git a/capi/cbindgen.toml b/capi/cbindgen.toml index aef9c6237b..d1a58234b5 100644 --- a/capi/cbindgen.toml +++ b/capi/cbindgen.toml @@ -1,4 +1,10 @@ +# See https://github.com/eqrion/cbindgen/blob/master/docs.md#cbindgentoml for +# a list of possible configuration values. language = "C" +header = """/* + * Copyright 2021 Sean McArthur. MIT License. + * Generated by gen_header.sh. Do not edit directly. + */""" include_guard = "_HYPER_H" no_includes = true sys_includes = ["stdint.h", "stddef.h"] diff --git a/capi/gen_header.sh b/capi/gen_header.sh index 4cd1a26c23..a3a02e1c60 100755 --- a/capi/gen_header.sh +++ b/capi/gen_header.sh @@ -1,9 +1,13 @@ #!/usr/bin/env bash -CAPI_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +# This script regenerates hyper.h. As of April 2021, it only works with the +# nightly build of Rust. + +set -e -WORK_DIR=`mktemp -d` +CAPI_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +WORK_DIR=$(mktemp -d) # check if tmp dir was created if [[ ! "$WORK_DIR" || ! -d "$WORK_DIR" ]]; then @@ -14,9 +18,8 @@ fi header_file_backup="$CAPI_DIR/include/hyper.h.backup" function cleanup { - #echo "$WORK_DIR" rm -rf "$WORK_DIR" - rm "$header_file_backup" + rm "$header_file_backup" || true } trap cleanup EXIT @@ -44,10 +47,14 @@ cp "$CAPI_DIR/include/hyper.h" "$header_file_backup" #cargo metadata --no-default-features --features ffi --format-version 1 > "$WORK_DIR/metadata.json" -cd $WORK_DIR +cd "${WORK_DIR}" || exit 2 # Expand just the ffi module -cargo rustc -- -Z unstable-options --pretty=expanded > expanded.rs 2>/dev/null +if ! output=$(cargo rustc -- -Z unstable-options --pretty=expanded 2>&1 > expanded.rs); then + # As of April 2021 the script above prints a lot of warnings/errors, and + # exits with a nonzero return code, but hyper.h still gets generated. + echo "$output" +fi # Replace the previous copy with the single expanded file rm -rf ./src @@ -56,17 +63,17 @@ mv expanded.rs src/lib.rs # Bindgen! -cbindgen\ - -c "$CAPI_DIR/cbindgen.toml"\ - --lockfile "$CAPI_DIR/../Cargo.lock"\ - -o "$CAPI_DIR/include/hyper.h"\ - $1 - -bindgen_exit_code=$? - -if [[ "--verify" == "$1" && "$bindgen_exit_code" != 0 ]]; then - echo "diff generated (<) vs backup (>)" - diff "$CAPI_DIR/include/hyper.h" "$header_file_backup" +if ! cbindgen \ + --config "$CAPI_DIR/cbindgen.toml" \ + --lockfile "$CAPI_DIR/../Cargo.lock" \ + --output "$CAPI_DIR/include/hyper.h" \ + "${@}"; then + bindgen_exit_code=$? + if [[ "--verify" == "$1" ]]; then + echo "diff generated (<) vs backup (>)" + diff "$CAPI_DIR/include/hyper.h" "$header_file_backup" + fi + exit $bindgen_exit_code fi -exit $bindgen_exit_code +exit 0 diff --git a/capi/include/hyper.h b/capi/include/hyper.h index cfc14a25fa..a305dc4a09 100644 --- a/capi/include/hyper.h +++ b/capi/include/hyper.h @@ -1,32 +1,78 @@ +/* + * Copyright 2021 Sean McArthur. MIT License. + * Generated by gen_header.sh. Do not edit directly. + */ + #ifndef _HYPER_H #define _HYPER_H #include #include +/* + Return in iter functions to continue iterating. + */ #define HYPER_ITER_CONTINUE 0 +/* + Return in iter functions to stop iterating. + */ #define HYPER_ITER_BREAK 1 +/* + An HTTP Version that is unspecified. + */ #define HYPER_HTTP_VERSION_NONE 0 +/* + The HTTP/1.0 version. + */ #define HYPER_HTTP_VERSION_1_0 10 +/* + The HTTP/1.1 version. + */ #define HYPER_HTTP_VERSION_1_1 11 +/* + The HTTP/2 version. + */ #define HYPER_HTTP_VERSION_2 20 +/* + Sentinel value to return from a read or write callback that the operation + is pending. + */ #define HYPER_IO_PENDING 4294967295 +/* + Sentinel value to return from a read or write callback that the operation + has errored. + */ #define HYPER_IO_ERROR 4294967294 +/* + Return in a poll function to indicate it was ready. + */ #define HYPER_POLL_READY 0 +/* + Return in a poll function to indicate it is still pending. + + The passed in `hyper_waker` should be registered to wake up the task at + some later point. + */ #define HYPER_POLL_PENDING 1 +/* + Return in a poll function indicate an error. + */ #define HYPER_POLL_ERROR 3 -typedef enum { +/* + A return code for many of hyper's methods. + */ +typedef enum hyper_code { /* All is well. */ @@ -60,7 +106,10 @@ typedef enum { HYPERE_INVALID_PEER_MESSAGE, } hyper_code; -typedef enum { +/* + A descriptor for what type a `hyper_task` value is. + */ +typedef enum hyper_task_return_type { /* The value of this task is null (does not imply an error). */ @@ -83,41 +132,86 @@ typedef enum { HYPER_TASK_BUF, } hyper_task_return_type; -typedef struct hyper_executor hyper_executor; - -typedef struct hyper_io hyper_io; - -typedef struct hyper_task hyper_task; - +/* + A streaming HTTP body. + */ typedef struct hyper_body hyper_body; +/* + A buffer of bytes that is sent or received on a `hyper_body`. + */ typedef struct hyper_buf hyper_buf; +/* + An HTTP client connection handle. + + These are used to send a request on a single connection. It's possible to + send multiple requests on a single connection, such as when HTTP/1 + keep-alive or HTTP/2 is used. + */ typedef struct hyper_clientconn hyper_clientconn; +/* + An options builder to configure an HTTP client connection. + */ typedef struct hyper_clientconn_options hyper_clientconn_options; +/* + An async context for a task that contains the related waker. + */ typedef struct hyper_context hyper_context; +/* + A more detailed error object returned by some hyper functions. + */ typedef struct hyper_error hyper_error; +/* + A task executor for `hyper_task`s. + */ +typedef struct hyper_executor hyper_executor; + +/* + An HTTP header map. + + These can be part of a request or response. + */ typedef struct hyper_headers hyper_headers; +/* + An IO object used to represent a socket or similar concept. + */ +typedef struct hyper_io hyper_io; + +/* + An HTTP request. + */ typedef struct hyper_request hyper_request; +/* + An HTTP response. + */ typedef struct hyper_response hyper_response; +/* + An async task. + */ +typedef struct hyper_task hyper_task; + +/* + A waker that is saved and used to waken a pending task. + */ typedef struct hyper_waker hyper_waker; -typedef int (*hyper_body_foreach_callback)(void*, const hyper_buf*); +typedef int (*hyper_body_foreach_callback)(void*, const struct hyper_buf*); -typedef int (*hyper_body_data_callback)(void*, hyper_context*, hyper_buf**); +typedef int (*hyper_body_data_callback)(void*, struct hyper_context*, struct hyper_buf**); typedef int (*hyper_headers_foreach_callback)(void*, const uint8_t*, size_t, const uint8_t*, size_t); -typedef size_t (*hyper_io_read_callback)(void*, hyper_context*, uint8_t*, size_t); +typedef size_t (*hyper_io_read_callback)(void*, struct hyper_context*, uint8_t*, size_t); -typedef size_t (*hyper_io_write_callback)(void*, hyper_context*, const uint8_t*, size_t); +typedef size_t (*hyper_io_write_callback)(void*, struct hyper_context*, const uint8_t*, size_t); #ifdef __cplusplus extern "C" { @@ -133,12 +227,12 @@ const char *hyper_version(void); If not configured, this body acts as an empty payload. */ -hyper_body *hyper_body_new(void); +struct hyper_body *hyper_body_new(void); /* Free a `hyper_body *`. */ -void hyper_body_free(hyper_body *body); +void hyper_body_free(struct hyper_body *body); /* Return a task that will poll the body for the next buffer of data. @@ -152,7 +246,7 @@ void hyper_body_free(hyper_body *body); This does not consume the `hyper_body *`, so it may be used to again. However, it MUST NOT be used or freed until the related task completes. */ -hyper_task *hyper_body_data(hyper_body *body); +struct hyper_task *hyper_body_data(struct hyper_body *body); /* Return a task that will poll the body and execute the callback with each @@ -166,12 +260,14 @@ hyper_task *hyper_body_data(hyper_body *body); This will consume the `hyper_body *`, you shouldn't use it anymore or free it. */ -hyper_task *hyper_body_foreach(hyper_body *body, hyper_body_foreach_callback func, void *userdata); +struct hyper_task *hyper_body_foreach(struct hyper_body *body, + hyper_body_foreach_callback func, + void *userdata); /* Set userdata on this body, which will be passed to callback functions. */ -void hyper_body_set_userdata(hyper_body *body, void *userdata); +void hyper_body_set_userdata(struct hyper_body *body, void *userdata); /* Set the data callback for this body. @@ -194,7 +290,7 @@ void hyper_body_set_userdata(hyper_body *body, void *userdata); If some error has occurred, you can return `HYPER_POLL_ERROR` to abort the body. */ -void hyper_body_set_data_func(hyper_body *body, hyper_body_data_callback func); +void hyper_body_set_data_func(struct hyper_body *body, hyper_body_data_callback func); /* Create a new `hyper_buf *` by copying the provided bytes. @@ -202,7 +298,7 @@ void hyper_body_set_data_func(hyper_body *body, hyper_body_data_callback func); This makes an owned copy of the bytes, so the `buf` argument can be freed or changed afterwards. */ -hyper_buf *hyper_buf_copy(const uint8_t *buf, size_t len); +struct hyper_buf *hyper_buf_copy(const uint8_t *buf, size_t len); /* Get a pointer to the bytes in this buffer. @@ -213,17 +309,17 @@ hyper_buf *hyper_buf_copy(const uint8_t *buf, size_t len); This pointer is borrowed data, and not valid once the `hyper_buf` is consumed/freed. */ -const uint8_t *hyper_buf_bytes(const hyper_buf *buf); +const uint8_t *hyper_buf_bytes(const struct hyper_buf *buf); /* Get the length of the bytes this buffer contains. */ -size_t hyper_buf_len(const hyper_buf *buf); +size_t hyper_buf_len(const struct hyper_buf *buf); /* Free this buffer. */ -void hyper_buf_free(hyper_buf *buf); +void hyper_buf_free(struct hyper_buf *buf); /* Starts an HTTP client connection handshake using the provided IO transport @@ -234,7 +330,8 @@ void hyper_buf_free(hyper_buf *buf); The returned `hyper_task *` must be polled with an executor until the handshake completes, at which point the value can be taken. */ -hyper_task *hyper_clientconn_handshake(hyper_io *io, hyper_clientconn_options *options); +struct hyper_task *hyper_clientconn_handshake(struct hyper_io *io, + struct hyper_clientconn_options *options); /* Send a request on the client connection. @@ -242,46 +339,47 @@ hyper_task *hyper_clientconn_handshake(hyper_io *io, hyper_clientconn_options *o Returns a task that needs to be polled until it is ready. When ready, the task yields a `hyper_response *`. */ -hyper_task *hyper_clientconn_send(hyper_clientconn *conn, hyper_request *req); +struct hyper_task *hyper_clientconn_send(struct hyper_clientconn *conn, struct hyper_request *req); /* Free a `hyper_clientconn *`. */ -void hyper_clientconn_free(hyper_clientconn *conn); +void hyper_clientconn_free(struct hyper_clientconn *conn); /* Creates a new set of HTTP clientconn options to be used in a handshake. */ -hyper_clientconn_options *hyper_clientconn_options_new(void); +struct hyper_clientconn_options *hyper_clientconn_options_new(void); /* Free a `hyper_clientconn_options *`. */ -void hyper_clientconn_options_free(hyper_clientconn_options *opts); +void hyper_clientconn_options_free(struct hyper_clientconn_options *opts); /* Set the client background task executor. This does not consume the `options` or the `exec`. */ -void hyper_clientconn_options_exec(hyper_clientconn_options *opts, const hyper_executor *exec); +void hyper_clientconn_options_exec(struct hyper_clientconn_options *opts, + const struct hyper_executor *exec); /* Set the whether to use HTTP2. Pass `0` to disable, `1` to enable. */ -hyper_code hyper_clientconn_options_http2(hyper_clientconn_options *opts, int enabled); +enum hyper_code hyper_clientconn_options_http2(struct hyper_clientconn_options *opts, int enabled); /* Frees a `hyper_error`. */ -void hyper_error_free(hyper_error *err); +void hyper_error_free(struct hyper_error *err); /* Get an equivalent `hyper_code` from this error. */ -hyper_code hyper_error_code(const hyper_error *err); +enum hyper_code hyper_error_code(const struct hyper_error *err); /* Print the details of this error to a buffer. @@ -291,27 +389,31 @@ hyper_code hyper_error_code(const hyper_error *err); The return value is number of bytes that were written to `dst`. */ -size_t hyper_error_print(const hyper_error *err, uint8_t *dst, size_t dst_len); +size_t hyper_error_print(const struct hyper_error *err, uint8_t *dst, size_t dst_len); /* Construct a new HTTP request. */ -hyper_request *hyper_request_new(void); +struct hyper_request *hyper_request_new(void); /* Free an HTTP request if not going to send it on a client. */ -void hyper_request_free(hyper_request *req); +void hyper_request_free(struct hyper_request *req); /* Set the HTTP Method of the request. */ -hyper_code hyper_request_set_method(hyper_request *req, const uint8_t *method, size_t method_len); +enum hyper_code hyper_request_set_method(struct hyper_request *req, + const uint8_t *method, + size_t method_len); /* Set the URI of the request. */ -hyper_code hyper_request_set_uri(hyper_request *req, const uint8_t *uri, size_t uri_len); +enum hyper_code hyper_request_set_uri(struct hyper_request *req, + const uint8_t *uri, + size_t uri_len); /* Set the preferred HTTP version of the request. @@ -321,7 +423,7 @@ hyper_code hyper_request_set_uri(hyper_request *req, const uint8_t *uri, size_t Note that this won't change the major HTTP version of the connection, since that is determined at the handshake step. */ -hyper_code hyper_request_set_version(hyper_request *req, int version); +enum hyper_code hyper_request_set_version(struct hyper_request *req, int version); /* Gets a reference to the HTTP headers of this request @@ -329,7 +431,7 @@ hyper_code hyper_request_set_version(hyper_request *req, int version); This is not an owned reference, so it should not be accessed after the `hyper_request` has been consumed. */ -hyper_headers *hyper_request_headers(hyper_request *req); +struct hyper_headers *hyper_request_headers(struct hyper_request *req); /* Set the body of the request. @@ -339,19 +441,19 @@ hyper_headers *hyper_request_headers(hyper_request *req); This takes ownership of the `hyper_body *`, you must not use it or free it after setting it on the request. */ -hyper_code hyper_request_set_body(hyper_request *req, hyper_body *body); +enum hyper_code hyper_request_set_body(struct hyper_request *req, struct hyper_body *body); /* Free an HTTP response after using it. */ -void hyper_response_free(hyper_response *resp); +void hyper_response_free(struct hyper_response *resp); /* Get the HTTP-Status code of this response. It will always be within the range of 100-599. */ -uint16_t hyper_response_status(const hyper_response *resp); +uint16_t hyper_response_status(const struct hyper_response *resp); /* Get a pointer to the reason-phrase of this response. @@ -364,14 +466,14 @@ uint16_t hyper_response_status(const hyper_response *resp); Use `hyper_response_reason_phrase_len()` to get the length of this buffer. */ -const uint8_t *hyper_response_reason_phrase(const hyper_response *resp); +const uint8_t *hyper_response_reason_phrase(const struct hyper_response *resp); /* Get the length of the reason-phrase of this response. Use `hyper_response_reason_phrase()` to get the buffer pointer. */ -size_t hyper_response_reason_phrase_len(const hyper_response *resp); +size_t hyper_response_reason_phrase_len(const struct hyper_response *resp); /* Get the HTTP version used by this response. @@ -383,7 +485,7 @@ size_t hyper_response_reason_phrase_len(const hyper_response *resp); - `HYPER_HTTP_VERSION_2` - `HYPER_HTTP_VERSION_NONE` if newer (or older). */ -int hyper_response_version(const hyper_response *resp); +int hyper_response_version(const struct hyper_response *resp); /* Gets a reference to the HTTP headers of this response. @@ -391,14 +493,14 @@ int hyper_response_version(const hyper_response *resp); This is not an owned reference, so it should not be accessed after the `hyper_response` has been freed. */ -hyper_headers *hyper_response_headers(hyper_response *resp); +struct hyper_headers *hyper_response_headers(struct hyper_response *resp); /* Take ownership of the body of this response. It is safe to free the response even after taking ownership of its body. */ -hyper_body *hyper_response_body(hyper_response *resp); +struct hyper_body *hyper_response_body(struct hyper_response *resp); /* Iterates the headers passing each name and value pair to the callback. @@ -408,7 +510,7 @@ hyper_body *hyper_response_body(hyper_response *resp); The callback should return `HYPER_ITER_CONTINUE` to keep iterating, or `HYPER_ITER_BREAK` to stop. */ -void hyper_headers_foreach(const hyper_headers *headers, +void hyper_headers_foreach(const struct hyper_headers *headers, hyper_headers_foreach_callback func, void *userdata); @@ -417,11 +519,11 @@ void hyper_headers_foreach(const hyper_headers *headers, This overwrites any previous value set for the header. */ -hyper_code hyper_headers_set(hyper_headers *headers, - const uint8_t *name, - size_t name_len, - const uint8_t *value, - size_t value_len); +enum hyper_code hyper_headers_set(struct hyper_headers *headers, + const uint8_t *name, + size_t name_len, + const uint8_t *value, + size_t value_len); /* Adds the provided value to the list of the provided name. @@ -429,11 +531,11 @@ hyper_code hyper_headers_set(hyper_headers *headers, If there were already existing values for the name, this will append the new value to the internal list. */ -hyper_code hyper_headers_add(hyper_headers *headers, - const uint8_t *name, - size_t name_len, - const uint8_t *value, - size_t value_len); +enum hyper_code hyper_headers_add(struct hyper_headers *headers, + const uint8_t *name, + size_t name_len, + const uint8_t *value, + size_t value_len); /* Create a new IO type used to represent a transport. @@ -441,7 +543,7 @@ hyper_code hyper_headers_add(hyper_headers *headers, The read and write functions of this transport should be set with `hyper_io_set_read` and `hyper_io_set_write`. */ -hyper_io *hyper_io_new(void); +struct hyper_io *hyper_io_new(void); /* Free an unused `hyper_io *`. @@ -449,14 +551,14 @@ hyper_io *hyper_io_new(void); This is typically only useful if you aren't going to pass ownership of the IO handle to hyper, such as with `hyper_clientconn_handshake()`. */ -void hyper_io_free(hyper_io *io); +void hyper_io_free(struct hyper_io *io); /* Set the user data pointer for this IO to some value. This value is passed as an argument to the read and write callbacks. */ -void hyper_io_set_userdata(hyper_io *io, void *data); +void hyper_io_set_userdata(struct hyper_io *io, void *data); /* Set the read function for this IO transport. @@ -476,7 +578,7 @@ void hyper_io_set_userdata(hyper_io *io, void *data); If there is an irrecoverable error reading data, then `HYPER_IO_ERROR` should be the return value. */ -void hyper_io_set_read(hyper_io *io, hyper_io_read_callback func); +void hyper_io_set_read(struct hyper_io *io, hyper_io_read_callback func); /* Set the write function for this IO transport. @@ -493,17 +595,17 @@ void hyper_io_set_read(hyper_io *io, hyper_io_read_callback func); If there is an irrecoverable error reading data, then `HYPER_IO_ERROR` should be the return value. */ -void hyper_io_set_write(hyper_io *io, hyper_io_write_callback func); +void hyper_io_set_write(struct hyper_io *io, hyper_io_write_callback func); /* Creates a new task executor. */ -const hyper_executor *hyper_executor_new(void); +const struct hyper_executor *hyper_executor_new(void); /* Frees an executor and any incomplete tasks still part of it. */ -void hyper_executor_free(const hyper_executor *exec); +void hyper_executor_free(const struct hyper_executor *exec); /* Push a task onto the executor. @@ -511,7 +613,7 @@ void hyper_executor_free(const hyper_executor *exec); The executor takes ownership of the task, it should not be accessed again unless returned back to the user with `hyper_executor_poll`. */ -hyper_code hyper_executor_push(const hyper_executor *exec, hyper_task *task); +enum hyper_code hyper_executor_push(const struct hyper_executor *exec, struct hyper_task *task); /* Polls the executor, trying to make progress on any tasks that have notified @@ -521,12 +623,12 @@ hyper_code hyper_executor_push(const hyper_executor *exec, hyper_task *task); If there are no ready tasks, this returns `NULL`. */ -hyper_task *hyper_executor_poll(const hyper_executor *exec); +struct hyper_task *hyper_executor_poll(const struct hyper_executor *exec); /* Free a task. */ -void hyper_task_free(hyper_task *task); +void hyper_task_free(struct hyper_task *task); /* Takes the output value of this task. @@ -536,12 +638,12 @@ void hyper_task_free(hyper_task *task); Use `hyper_task_type` to determine the type of the `void *` return value. */ -void *hyper_task_value(hyper_task *task); +void *hyper_task_value(struct hyper_task *task); /* Query the return type of this task. */ -hyper_task_return_type hyper_task_type(hyper_task *task); +enum hyper_task_return_type hyper_task_type(struct hyper_task *task); /* Set a user data pointer to be associated with this task. @@ -549,27 +651,27 @@ hyper_task_return_type hyper_task_type(hyper_task *task); This value will be passed to task callbacks, and can be checked later with `hyper_task_userdata`. */ -void hyper_task_set_userdata(hyper_task *task, void *userdata); +void hyper_task_set_userdata(struct hyper_task *task, void *userdata); /* Retrieve the userdata that has been set via `hyper_task_set_userdata`. */ -void *hyper_task_userdata(hyper_task *task); +void *hyper_task_userdata(struct hyper_task *task); /* Copies a waker out of the task context. */ -hyper_waker *hyper_context_waker(hyper_context *cx); +struct hyper_waker *hyper_context_waker(struct hyper_context *cx); /* Free a waker that hasn't been woken. */ -void hyper_waker_free(hyper_waker *waker); +void hyper_waker_free(struct hyper_waker *waker); /* Free a waker that hasn't been woken. */ -void hyper_waker_wake(hyper_waker *waker); +void hyper_waker_wake(struct hyper_waker *waker); #ifdef __cplusplus } // extern "C"