mirror of
https://github.com/Noratrieb/icefun.git
synced 2026-01-15 05:05:01 +01:00
private
This commit is contained in:
parent
25adea4103
commit
7af1274587
160 changed files with 38999 additions and 4 deletions
17
hyper/capi/README.md
Normal file
17
hyper/capi/README.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# C API for hyper
|
||||
|
||||
This provides auxiliary pieces for a C API to use the hyper library.
|
||||
|
||||
## Unstable
|
||||
|
||||
The C API of hyper is currently **unstable**, which means it's not part of the semver contract as the rest of the Rust API is.
|
||||
|
||||
Because of that, it's only accessible if `--cfg hyper_unstable_ffi` is passed to `rustc` when compiling. The easiest way to do that is setting the `RUSTFLAGS` environment variable.
|
||||
|
||||
## Building
|
||||
|
||||
The C API is part of the Rust library, but isn't compiled by default. Using a nightly release of `cargo`, starting with `nightly-2022-03-02`, it can be compiled with the following command:
|
||||
|
||||
```
|
||||
RUSTFLAGS="--cfg hyper_unstable_ffi" cargo +nightly rustc --features client,http1,http2,ffi -Z unstable-options --crate-type cdylib
|
||||
```
|
||||
15
hyper/capi/cbindgen.toml
Normal file
15
hyper/capi/cbindgen.toml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# 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"]
|
||||
cpp_compat = true
|
||||
documentation_style = "c"
|
||||
|
||||
[parse.expand]
|
||||
crates = ["hyper-capi"]
|
||||
25
hyper/capi/examples/Makefile
Normal file
25
hyper/capi/examples/Makefile
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#
|
||||
# Build the example client
|
||||
#
|
||||
|
||||
TARGET = client
|
||||
TARGET2 = upload
|
||||
|
||||
OBJS = client.o
|
||||
OBJS2 = upload.o
|
||||
|
||||
RPATH=$(PWD)/../../target/debug
|
||||
CFLAGS = -I../include
|
||||
LDFLAGS = -L$(RPATH) -Wl,-rpath,$(RPATH)
|
||||
LIBS = -lhyper
|
||||
|
||||
all: $(TARGET) $(TARGET2)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(CC) -o $(TARGET) $(OBJS) $(LDFLAGS) $(LIBS)
|
||||
|
||||
$(TARGET2): $(OBJS2)
|
||||
$(CC) -o $(TARGET2) $(OBJS2) $(LDFLAGS) $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET) $(OBJS2) $(TARGET2)
|
||||
343
hyper/capi/examples/client.c
Normal file
343
hyper/capi/examples/client.c
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/select.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hyper.h"
|
||||
|
||||
|
||||
struct conn_data {
|
||||
int fd;
|
||||
hyper_waker *read_waker;
|
||||
hyper_waker *write_waker;
|
||||
};
|
||||
|
||||
static size_t read_cb(void *userdata, hyper_context *ctx, uint8_t *buf, size_t buf_len) {
|
||||
struct conn_data *conn = (struct conn_data *)userdata;
|
||||
ssize_t ret = read(conn->fd, buf, buf_len);
|
||||
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (errno != EAGAIN) {
|
||||
// kaboom
|
||||
return HYPER_IO_ERROR;
|
||||
}
|
||||
|
||||
// would block, register interest
|
||||
if (conn->read_waker != NULL) {
|
||||
hyper_waker_free(conn->read_waker);
|
||||
}
|
||||
conn->read_waker = hyper_context_waker(ctx);
|
||||
return HYPER_IO_PENDING;
|
||||
}
|
||||
|
||||
static size_t write_cb(void *userdata, hyper_context *ctx, const uint8_t *buf, size_t buf_len) {
|
||||
struct conn_data *conn = (struct conn_data *)userdata;
|
||||
ssize_t ret = write(conn->fd, buf, buf_len);
|
||||
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (errno != EAGAIN) {
|
||||
// kaboom
|
||||
return HYPER_IO_ERROR;
|
||||
}
|
||||
|
||||
// would block, register interest
|
||||
if (conn->write_waker != NULL) {
|
||||
hyper_waker_free(conn->write_waker);
|
||||
}
|
||||
conn->write_waker = hyper_context_waker(ctx);
|
||||
return HYPER_IO_PENDING;
|
||||
}
|
||||
|
||||
static void free_conn_data(struct conn_data *conn) {
|
||||
if (conn->read_waker) {
|
||||
hyper_waker_free(conn->read_waker);
|
||||
conn->read_waker = NULL;
|
||||
}
|
||||
if (conn->write_waker) {
|
||||
hyper_waker_free(conn->write_waker);
|
||||
conn->write_waker = NULL;
|
||||
}
|
||||
|
||||
free(conn);
|
||||
}
|
||||
|
||||
static int connect_to(const char *host, const char *port) {
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
struct addrinfo *result, *rp;
|
||||
if (getaddrinfo(host, port, &hints, &result) != 0) {
|
||||
printf("dns failed for %s\n", host);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sfd;
|
||||
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
||||
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (sfd == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
close(sfd);
|
||||
}
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
// no address succeeded
|
||||
if (rp == NULL) {
|
||||
printf("connect failed for %s\n", host);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sfd;
|
||||
}
|
||||
|
||||
static int print_each_header(void *userdata,
|
||||
const uint8_t *name,
|
||||
size_t name_len,
|
||||
const uint8_t *value,
|
||||
size_t value_len) {
|
||||
printf("%.*s: %.*s\n", (int) name_len, name, (int) value_len, value);
|
||||
return HYPER_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static int print_each_chunk(void *userdata, const hyper_buf *chunk) {
|
||||
const uint8_t *buf = hyper_buf_bytes(chunk);
|
||||
size_t len = hyper_buf_len(chunk);
|
||||
|
||||
write(1, buf, len);
|
||||
|
||||
return HYPER_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
EXAMPLE_NOT_SET = 0, // tasks we don't know about won't have a userdata set
|
||||
EXAMPLE_HANDSHAKE,
|
||||
EXAMPLE_SEND,
|
||||
EXAMPLE_RESP_BODY
|
||||
} example_id;
|
||||
|
||||
#define STR_ARG(XX) (uint8_t *)XX, strlen(XX)
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
const char *host = argc > 1 ? argv[1] : "httpbin.org";
|
||||
const char *port = argc > 2 ? argv[2] : "80";
|
||||
const char *path = argc > 3 ? argv[3] : "/";
|
||||
printf("connecting to port %s on %s...\n", port, host);
|
||||
|
||||
int fd = connect_to(host, port);
|
||||
if (fd < 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("connected to %s, now get %s\n", host, path);
|
||||
if (fcntl(fd, F_SETFL, O_NONBLOCK) != 0) {
|
||||
printf("failed to set socket to non-blocking\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fd_set fds_read;
|
||||
fd_set fds_write;
|
||||
fd_set fds_excep;
|
||||
|
||||
struct conn_data *conn = malloc(sizeof(struct conn_data));
|
||||
|
||||
conn->fd = fd;
|
||||
conn->read_waker = NULL;
|
||||
conn->write_waker = NULL;
|
||||
|
||||
// Hookup the IO
|
||||
hyper_io *io = hyper_io_new();
|
||||
hyper_io_set_userdata(io, (void *)conn);
|
||||
hyper_io_set_read(io, read_cb);
|
||||
hyper_io_set_write(io, write_cb);
|
||||
|
||||
printf("http handshake (hyper v%s) ...\n", hyper_version());
|
||||
|
||||
// We need an executor generally to poll futures
|
||||
const hyper_executor *exec = hyper_executor_new();
|
||||
|
||||
// Prepare client options
|
||||
hyper_clientconn_options *opts = hyper_clientconn_options_new();
|
||||
hyper_clientconn_options_exec(opts, exec);
|
||||
|
||||
hyper_task *handshake = hyper_clientconn_handshake(io, opts);
|
||||
hyper_task_set_userdata(handshake, (void *)EXAMPLE_HANDSHAKE);
|
||||
|
||||
// Let's wait for the handshake to finish...
|
||||
hyper_executor_push(exec, handshake);
|
||||
|
||||
// In case a task errors...
|
||||
hyper_error *err;
|
||||
|
||||
// The polling state machine!
|
||||
while (1) {
|
||||
// Poll all ready tasks and act on them...
|
||||
while (1) {
|
||||
hyper_task *task = hyper_executor_poll(exec);
|
||||
if (!task) {
|
||||
break;
|
||||
}
|
||||
switch ((example_id) hyper_task_userdata(task)) {
|
||||
case EXAMPLE_HANDSHAKE:
|
||||
;
|
||||
if (hyper_task_type(task) == HYPER_TASK_ERROR) {
|
||||
printf("handshake error!\n");
|
||||
err = hyper_task_value(task);
|
||||
goto fail;
|
||||
}
|
||||
assert(hyper_task_type(task) == HYPER_TASK_CLIENTCONN);
|
||||
|
||||
printf("preparing http request ...\n");
|
||||
|
||||
hyper_clientconn *client = hyper_task_value(task);
|
||||
hyper_task_free(task);
|
||||
|
||||
// Prepare the request
|
||||
hyper_request *req = hyper_request_new();
|
||||
if (hyper_request_set_method(req, STR_ARG("GET"))) {
|
||||
printf("error setting method\n");
|
||||
return 1;
|
||||
}
|
||||
if (hyper_request_set_uri(req, STR_ARG(path))) {
|
||||
printf("error setting uri\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
hyper_headers *req_headers = hyper_request_headers(req);
|
||||
hyper_headers_set(req_headers, STR_ARG("Host"), STR_ARG(host));
|
||||
|
||||
// Send it!
|
||||
hyper_task *send = hyper_clientconn_send(client, req);
|
||||
hyper_task_set_userdata(send, (void *)EXAMPLE_SEND);
|
||||
printf("sending ...\n");
|
||||
hyper_executor_push(exec, send);
|
||||
|
||||
// For this example, no longer need the client
|
||||
hyper_clientconn_free(client);
|
||||
|
||||
break;
|
||||
case EXAMPLE_SEND:
|
||||
;
|
||||
if (hyper_task_type(task) == HYPER_TASK_ERROR) {
|
||||
printf("send error!\n");
|
||||
err = hyper_task_value(task);
|
||||
goto fail;
|
||||
}
|
||||
assert(hyper_task_type(task) == HYPER_TASK_RESPONSE);
|
||||
|
||||
// Take the results
|
||||
hyper_response *resp = hyper_task_value(task);
|
||||
hyper_task_free(task);
|
||||
|
||||
uint16_t http_status = hyper_response_status(resp);
|
||||
const uint8_t *rp = hyper_response_reason_phrase(resp);
|
||||
size_t rp_len = hyper_response_reason_phrase_len(resp);
|
||||
|
||||
printf("\nResponse Status: %d %.*s\n", http_status, (int) rp_len, rp);
|
||||
|
||||
hyper_headers *headers = hyper_response_headers(resp);
|
||||
hyper_headers_foreach(headers, print_each_header, NULL);
|
||||
printf("\n");
|
||||
|
||||
hyper_body *resp_body = hyper_response_body(resp);
|
||||
hyper_task *foreach = hyper_body_foreach(resp_body, print_each_chunk, NULL);
|
||||
hyper_task_set_userdata(foreach, (void *)EXAMPLE_RESP_BODY);
|
||||
hyper_executor_push(exec, foreach);
|
||||
|
||||
// No longer need the response
|
||||
hyper_response_free(resp);
|
||||
|
||||
break;
|
||||
case EXAMPLE_RESP_BODY:
|
||||
;
|
||||
if (hyper_task_type(task) == HYPER_TASK_ERROR) {
|
||||
printf("body error!\n");
|
||||
err = hyper_task_value(task);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
assert(hyper_task_type(task) == HYPER_TASK_EMPTY);
|
||||
|
||||
printf("\n -- Done! -- \n");
|
||||
|
||||
// Cleaning up before exiting
|
||||
hyper_task_free(task);
|
||||
hyper_executor_free(exec);
|
||||
free_conn_data(conn);
|
||||
|
||||
return 0;
|
||||
case EXAMPLE_NOT_SET:
|
||||
// A background task for hyper completed...
|
||||
hyper_task_free(task);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// All futures are pending on IO work, so select on the fds.
|
||||
|
||||
FD_ZERO(&fds_read);
|
||||
FD_ZERO(&fds_write);
|
||||
FD_ZERO(&fds_excep);
|
||||
|
||||
if (conn->read_waker) {
|
||||
FD_SET(conn->fd, &fds_read);
|
||||
}
|
||||
if (conn->write_waker) {
|
||||
FD_SET(conn->fd, &fds_write);
|
||||
}
|
||||
|
||||
int sel_ret = select(conn->fd + 1, &fds_read, &fds_write, &fds_excep, NULL);
|
||||
|
||||
if (sel_ret < 0) {
|
||||
printf("select() error\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (FD_ISSET(conn->fd, &fds_read)) {
|
||||
hyper_waker_wake(conn->read_waker);
|
||||
conn->read_waker = NULL;
|
||||
}
|
||||
|
||||
if (FD_ISSET(conn->fd, &fds_write)) {
|
||||
hyper_waker_wake(conn->write_waker);
|
||||
conn->write_waker = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (err) {
|
||||
printf("error code: %d\n", hyper_error_code(err));
|
||||
// grab the error details
|
||||
char errbuf [256];
|
||||
size_t errlen = hyper_error_print(err, errbuf, sizeof(errbuf));
|
||||
printf("details: %.*s\n", (int) errlen, errbuf);
|
||||
|
||||
// clean up the error
|
||||
hyper_error_free(err);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
406
hyper/capi/examples/upload.c
Normal file
406
hyper/capi/examples/upload.c
Normal file
|
|
@ -0,0 +1,406 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/select.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hyper.h"
|
||||
|
||||
|
||||
struct conn_data {
|
||||
int fd;
|
||||
hyper_waker *read_waker;
|
||||
hyper_waker *write_waker;
|
||||
};
|
||||
|
||||
static size_t read_cb(void *userdata, hyper_context *ctx, uint8_t *buf, size_t buf_len) {
|
||||
struct conn_data *conn = (struct conn_data *)userdata;
|
||||
ssize_t ret = read(conn->fd, buf, buf_len);
|
||||
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (errno != EAGAIN) {
|
||||
// kaboom
|
||||
return HYPER_IO_ERROR;
|
||||
}
|
||||
|
||||
// would block, register interest
|
||||
if (conn->read_waker != NULL) {
|
||||
hyper_waker_free(conn->read_waker);
|
||||
}
|
||||
conn->read_waker = hyper_context_waker(ctx);
|
||||
return HYPER_IO_PENDING;
|
||||
}
|
||||
|
||||
static size_t write_cb(void *userdata, hyper_context *ctx, const uint8_t *buf, size_t buf_len) {
|
||||
struct conn_data *conn = (struct conn_data *)userdata;
|
||||
ssize_t ret = write(conn->fd, buf, buf_len);
|
||||
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (errno != EAGAIN) {
|
||||
// kaboom
|
||||
return HYPER_IO_ERROR;
|
||||
}
|
||||
|
||||
// would block, register interest
|
||||
if (conn->write_waker != NULL) {
|
||||
hyper_waker_free(conn->write_waker);
|
||||
}
|
||||
conn->write_waker = hyper_context_waker(ctx);
|
||||
return HYPER_IO_PENDING;
|
||||
}
|
||||
|
||||
static void free_conn_data(struct conn_data *conn) {
|
||||
if (conn->read_waker) {
|
||||
hyper_waker_free(conn->read_waker);
|
||||
conn->read_waker = NULL;
|
||||
}
|
||||
if (conn->write_waker) {
|
||||
hyper_waker_free(conn->write_waker);
|
||||
conn->write_waker = NULL;
|
||||
}
|
||||
|
||||
free(conn);
|
||||
}
|
||||
|
||||
static int connect_to(const char *host, const char *port) {
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
struct addrinfo *result, *rp;
|
||||
if (getaddrinfo(host, port, &hints, &result) != 0) {
|
||||
printf("dns failed for %s\n", host);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sfd;
|
||||
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
||||
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (sfd == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
close(sfd);
|
||||
}
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
// no address succeeded
|
||||
if (rp == NULL) {
|
||||
printf("connect failed for %s\n", host);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sfd;
|
||||
}
|
||||
|
||||
struct upload_body {
|
||||
int fd;
|
||||
char *buf;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
static int poll_req_upload(void *userdata,
|
||||
hyper_context *ctx,
|
||||
hyper_buf **chunk) {
|
||||
struct upload_body* upload = userdata;
|
||||
|
||||
ssize_t res = read(upload->fd, upload->buf, upload->len);
|
||||
if (res > 0) {
|
||||
*chunk = hyper_buf_copy(upload->buf, res);
|
||||
return HYPER_POLL_READY;
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
// All done!
|
||||
*chunk = NULL;
|
||||
return HYPER_POLL_READY;
|
||||
}
|
||||
|
||||
// Oh no!
|
||||
printf("error reading upload file: %d", errno);
|
||||
return HYPER_POLL_ERROR;
|
||||
}
|
||||
|
||||
static int print_each_header(void *userdata,
|
||||
const uint8_t *name,
|
||||
size_t name_len,
|
||||
const uint8_t *value,
|
||||
size_t value_len) {
|
||||
printf("%.*s: %.*s\n", (int) name_len, name, (int) value_len, value);
|
||||
return HYPER_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static void print_informational(void *userdata, hyper_response *resp) {
|
||||
uint16_t http_status = hyper_response_status(resp);
|
||||
|
||||
printf("\nInformational (1xx): %d\n", http_status);
|
||||
|
||||
const hyper_buf *headers = hyper_response_headers_raw(resp);
|
||||
if (headers) {
|
||||
write(1, hyper_buf_bytes(headers), hyper_buf_len(headers));
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
EXAMPLE_NOT_SET = 0, // tasks we don't know about won't have a userdata set
|
||||
EXAMPLE_HANDSHAKE,
|
||||
EXAMPLE_SEND,
|
||||
EXAMPLE_RESP_BODY
|
||||
} example_id;
|
||||
|
||||
#define STR_ARG(XX) (uint8_t *)XX, strlen(XX)
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
const char *file = argc > 1 ? argv[1] : NULL;
|
||||
const char *host = argc > 2 ? argv[2] : "httpbin.org";
|
||||
const char *port = argc > 3 ? argv[3] : "80";
|
||||
const char *path = argc > 4 ? argv[4] : "/post";
|
||||
|
||||
if (!file) {
|
||||
printf("Pass a file path as the first argument.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct upload_body upload;
|
||||
upload.fd = open(file, O_RDONLY);
|
||||
|
||||
if (upload.fd < 0) {
|
||||
printf("error opening file to upload: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
printf("connecting to port %s on %s...\n", port, host);
|
||||
|
||||
int fd = connect_to(host, port);
|
||||
if (fd < 0) {
|
||||
return 1;
|
||||
}
|
||||
printf("connected to %s, now upload to %s\n", host, path);
|
||||
|
||||
if (fcntl(fd, F_SETFL, O_NONBLOCK) != 0) {
|
||||
printf("failed to set socket to non-blocking\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
upload.len = 8192;
|
||||
upload.buf = malloc(upload.len);
|
||||
|
||||
fd_set fds_read;
|
||||
fd_set fds_write;
|
||||
fd_set fds_excep;
|
||||
|
||||
struct conn_data *conn = malloc(sizeof(struct conn_data));
|
||||
|
||||
conn->fd = fd;
|
||||
conn->read_waker = NULL;
|
||||
conn->write_waker = NULL;
|
||||
|
||||
|
||||
// Hookup the IO
|
||||
hyper_io *io = hyper_io_new();
|
||||
hyper_io_set_userdata(io, (void *)conn);
|
||||
hyper_io_set_read(io, read_cb);
|
||||
hyper_io_set_write(io, write_cb);
|
||||
|
||||
printf("http handshake (hyper v%s) ...\n", hyper_version());
|
||||
|
||||
// We need an executor generally to poll futures
|
||||
const hyper_executor *exec = hyper_executor_new();
|
||||
|
||||
// Prepare client options
|
||||
hyper_clientconn_options *opts = hyper_clientconn_options_new();
|
||||
hyper_clientconn_options_exec(opts, exec);
|
||||
hyper_clientconn_options_headers_raw(opts, 1);
|
||||
|
||||
hyper_task *handshake = hyper_clientconn_handshake(io, opts);
|
||||
hyper_task_set_userdata(handshake, (void *)EXAMPLE_HANDSHAKE);
|
||||
|
||||
// Let's wait for the handshake to finish...
|
||||
hyper_executor_push(exec, handshake);
|
||||
|
||||
// This body will get filled in eventually...
|
||||
hyper_body *resp_body = NULL;
|
||||
|
||||
// The polling state machine!
|
||||
while (1) {
|
||||
// Poll all ready tasks and act on them...
|
||||
while (1) {
|
||||
hyper_task *task = hyper_executor_poll(exec);
|
||||
if (!task) {
|
||||
break;
|
||||
}
|
||||
hyper_task_return_type task_type = hyper_task_type(task);
|
||||
|
||||
switch ((example_id) hyper_task_userdata(task)) {
|
||||
case EXAMPLE_HANDSHAKE:
|
||||
;
|
||||
if (task_type == HYPER_TASK_ERROR) {
|
||||
printf("handshake error!\n");
|
||||
return 1;
|
||||
}
|
||||
assert(task_type == HYPER_TASK_CLIENTCONN);
|
||||
|
||||
printf("preparing http request ...\n");
|
||||
|
||||
hyper_clientconn *client = hyper_task_value(task);
|
||||
hyper_task_free(task);
|
||||
|
||||
// Prepare the request
|
||||
hyper_request *req = hyper_request_new();
|
||||
if (hyper_request_set_method(req, STR_ARG("POST"))) {
|
||||
printf("error setting method\n");
|
||||
return 1;
|
||||
}
|
||||
if (hyper_request_set_uri(req, STR_ARG(path))) {
|
||||
printf("error setting uri\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
hyper_headers *req_headers = hyper_request_headers(req);
|
||||
hyper_headers_set(req_headers, STR_ARG("host"), STR_ARG(host));
|
||||
hyper_headers_set(req_headers, STR_ARG("expect"), STR_ARG("100-continue"));
|
||||
|
||||
// NOTE: We aren't handling *waiting* for the 100 Continue,
|
||||
// the body is sent immediately. This will just print if any
|
||||
// informational headers are received.
|
||||
printf(" with expect-continue ...\n");
|
||||
hyper_request_on_informational(req, print_informational, NULL);
|
||||
|
||||
// Prepare the req body
|
||||
hyper_body *body = hyper_body_new();
|
||||
hyper_body_set_userdata(body, &upload);
|
||||
hyper_body_set_data_func(body, poll_req_upload);
|
||||
hyper_request_set_body(req, body);
|
||||
|
||||
// Send it!
|
||||
hyper_task *send = hyper_clientconn_send(client, req);
|
||||
hyper_task_set_userdata(send, (void *)EXAMPLE_SEND);
|
||||
printf("sending ...\n");
|
||||
hyper_executor_push(exec, send);
|
||||
|
||||
// For this example, no longer need the client
|
||||
hyper_clientconn_free(client);
|
||||
|
||||
break;
|
||||
case EXAMPLE_SEND:
|
||||
;
|
||||
if (task_type == HYPER_TASK_ERROR) {
|
||||
printf("send error!\n");
|
||||
return 1;
|
||||
}
|
||||
assert(task_type == HYPER_TASK_RESPONSE);
|
||||
|
||||
// Take the results
|
||||
hyper_response *resp = hyper_task_value(task);
|
||||
hyper_task_free(task);
|
||||
|
||||
uint16_t http_status = hyper_response_status(resp);
|
||||
|
||||
printf("\nResponse Status: %d\n", http_status);
|
||||
|
||||
hyper_headers *headers = hyper_response_headers(resp);
|
||||
hyper_headers_foreach(headers, print_each_header, NULL);
|
||||
printf("\n");
|
||||
|
||||
resp_body = hyper_response_body(resp);
|
||||
|
||||
// Set us up to peel data from the body a chunk at a time
|
||||
hyper_task *body_data = hyper_body_data(resp_body);
|
||||
hyper_task_set_userdata(body_data, (void *)EXAMPLE_RESP_BODY);
|
||||
hyper_executor_push(exec, body_data);
|
||||
|
||||
// No longer need the response
|
||||
hyper_response_free(resp);
|
||||
|
||||
break;
|
||||
case EXAMPLE_RESP_BODY:
|
||||
;
|
||||
if (task_type == HYPER_TASK_ERROR) {
|
||||
printf("body error!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (task_type == HYPER_TASK_BUF) {
|
||||
hyper_buf *chunk = hyper_task_value(task);
|
||||
write(1, hyper_buf_bytes(chunk), hyper_buf_len(chunk));
|
||||
hyper_buf_free(chunk);
|
||||
hyper_task_free(task);
|
||||
|
||||
hyper_task *body_data = hyper_body_data(resp_body);
|
||||
hyper_task_set_userdata(body_data, (void *)EXAMPLE_RESP_BODY);
|
||||
hyper_executor_push(exec, body_data);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
assert(task_type == HYPER_TASK_EMPTY);
|
||||
hyper_task_free(task);
|
||||
hyper_body_free(resp_body);
|
||||
|
||||
printf("\n -- Done! -- \n");
|
||||
|
||||
// Cleaning up before exiting
|
||||
hyper_executor_free(exec);
|
||||
free_conn_data(conn);
|
||||
free(upload.buf);
|
||||
|
||||
return 0;
|
||||
case EXAMPLE_NOT_SET:
|
||||
// A background task for hyper completed...
|
||||
hyper_task_free(task);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// All futures are pending on IO work, so select on the fds.
|
||||
|
||||
FD_ZERO(&fds_read);
|
||||
FD_ZERO(&fds_write);
|
||||
FD_ZERO(&fds_excep);
|
||||
|
||||
if (conn->read_waker) {
|
||||
FD_SET(conn->fd, &fds_read);
|
||||
}
|
||||
if (conn->write_waker) {
|
||||
FD_SET(conn->fd, &fds_write);
|
||||
}
|
||||
|
||||
int sel_ret = select(conn->fd + 1, &fds_read, &fds_write, &fds_excep, NULL);
|
||||
|
||||
if (sel_ret < 0) {
|
||||
printf("select() error\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (FD_ISSET(conn->fd, &fds_read)) {
|
||||
hyper_waker_wake(conn->read_waker);
|
||||
conn->read_waker = NULL;
|
||||
}
|
||||
|
||||
if (FD_ISSET(conn->fd, &fds_write)) {
|
||||
hyper_waker_wake(conn->write_waker);
|
||||
conn->write_waker = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
108
hyper/capi/gen_header.sh
Executable file
108
hyper/capi/gen_header.sh
Executable file
|
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# This script regenerates hyper.h. As of April 2021, it only works with the
|
||||
# nightly build of Rust.
|
||||
|
||||
set -e
|
||||
|
||||
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
|
||||
echo "Could not create temp dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
header_file_backup="$CAPI_DIR/include/hyper.h.backup"
|
||||
|
||||
function cleanup {
|
||||
rm -rf "$WORK_DIR"
|
||||
rm "$header_file_backup" || true
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
mkdir "$WORK_DIR/src"
|
||||
|
||||
# Fake a library
|
||||
cat > "$WORK_DIR/src/lib.rs" << EOF
|
||||
#[path = "$CAPI_DIR/../src/ffi/mod.rs"]
|
||||
pub mod ffi;
|
||||
EOF
|
||||
|
||||
# And its Cargo.toml
|
||||
cat > "$WORK_DIR/Cargo.toml" << EOF
|
||||
[package]
|
||||
name = "hyper"
|
||||
version = "0.0.0"
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
# Determined which dependencies we need by running the "cargo rustc" command
|
||||
# below and watching the compile error output for references to unknown imports,
|
||||
# until we didn't get any errors.
|
||||
bytes = "1"
|
||||
futures-channel = "0.3"
|
||||
futures-util = { version = "0.3", default-features = false, features = ["alloc"] }
|
||||
libc = { version = "0.2", optional = true }
|
||||
http = "0.2"
|
||||
http-body = "0.4"
|
||||
tokio = { version = "1", features = ["rt"] }
|
||||
|
||||
[features]
|
||||
default = [
|
||||
"client",
|
||||
"ffi",
|
||||
"http1",
|
||||
]
|
||||
|
||||
http1 = []
|
||||
client = []
|
||||
ffi = ["libc", "tokio/rt"]
|
||||
EOF
|
||||
|
||||
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}" || exit 2
|
||||
|
||||
# Expand just the ffi module
|
||||
if ! output=$(RUSTFLAGS='--cfg hyper_unstable_ffi' cargo rustc -- -Z unpretty=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.
|
||||
#
|
||||
# However, on Github Actions, this will result in automatic "annotations"
|
||||
# being added to files not related to a PR, so if this is `--verify` mode,
|
||||
# then don't show it.
|
||||
#
|
||||
# But yes show it when using it locally.
|
||||
if [[ "--verify" != "$1" ]]; then
|
||||
echo "$output"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Replace the previous copy with the single expanded file
|
||||
rm -rf ./src
|
||||
mkdir src
|
||||
mv expanded.rs src/lib.rs
|
||||
|
||||
|
||||
# Bindgen!
|
||||
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 0
|
||||
779
hyper/capi/include/hyper.h
Normal file
779
hyper/capi/include/hyper.h
Normal file
|
|
@ -0,0 +1,779 @@
|
|||
/*
|
||||
* Copyright 2021 Sean McArthur. MIT License.
|
||||
* Generated by gen_header.sh. Do not edit directly.
|
||||
*/
|
||||
|
||||
#ifndef _HYPER_H
|
||||
#define _HYPER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
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
|
||||
|
||||
/*
|
||||
A return code for many of hyper's methods.
|
||||
*/
|
||||
typedef enum hyper_code {
|
||||
/*
|
||||
All is well.
|
||||
*/
|
||||
HYPERE_OK,
|
||||
/*
|
||||
General error, details in the `hyper_error *`.
|
||||
*/
|
||||
HYPERE_ERROR,
|
||||
/*
|
||||
A function argument was invalid.
|
||||
*/
|
||||
HYPERE_INVALID_ARG,
|
||||
/*
|
||||
The IO transport returned an EOF when one wasn't expected.
|
||||
|
||||
This typically means an HTTP request or response was expected, but the
|
||||
connection closed cleanly without sending (all of) it.
|
||||
*/
|
||||
HYPERE_UNEXPECTED_EOF,
|
||||
/*
|
||||
Aborted by a user supplied callback.
|
||||
*/
|
||||
HYPERE_ABORTED_BY_CALLBACK,
|
||||
/*
|
||||
An optional hyper feature was not enabled.
|
||||
*/
|
||||
HYPERE_FEATURE_NOT_ENABLED,
|
||||
/*
|
||||
The peer sent an HTTP message that could not be parsed.
|
||||
*/
|
||||
HYPERE_INVALID_PEER_MESSAGE,
|
||||
} hyper_code;
|
||||
|
||||
/*
|
||||
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).
|
||||
*/
|
||||
HYPER_TASK_EMPTY,
|
||||
/*
|
||||
The value of this task is `hyper_error *`.
|
||||
*/
|
||||
HYPER_TASK_ERROR,
|
||||
/*
|
||||
The value of this task is `hyper_clientconn *`.
|
||||
*/
|
||||
HYPER_TASK_CLIENTCONN,
|
||||
/*
|
||||
The value of this task is `hyper_response *`.
|
||||
*/
|
||||
HYPER_TASK_RESPONSE,
|
||||
/*
|
||||
The value of this task is `hyper_buf *`.
|
||||
*/
|
||||
HYPER_TASK_BUF,
|
||||
} hyper_task_return_type;
|
||||
|
||||
/*
|
||||
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 struct hyper_buf*);
|
||||
|
||||
typedef int (*hyper_body_data_callback)(void*, struct hyper_context*, struct hyper_buf**);
|
||||
|
||||
typedef void (*hyper_request_on_informational_callback)(void*, struct hyper_response*);
|
||||
|
||||
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*, struct hyper_context*, uint8_t*, size_t);
|
||||
|
||||
typedef size_t (*hyper_io_write_callback)(void*, struct hyper_context*, const uint8_t*, size_t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
/*
|
||||
Returns a static ASCII (null terminated) string of the hyper version.
|
||||
*/
|
||||
const char *hyper_version(void);
|
||||
|
||||
/*
|
||||
Create a new "empty" body.
|
||||
|
||||
If not configured, this body acts as an empty payload.
|
||||
*/
|
||||
struct hyper_body *hyper_body_new(void);
|
||||
|
||||
/*
|
||||
Free a `hyper_body *`.
|
||||
*/
|
||||
void hyper_body_free(struct hyper_body *body);
|
||||
|
||||
/*
|
||||
Return a task that will poll the body for the next buffer of data.
|
||||
|
||||
The task value may have different types depending on the outcome:
|
||||
|
||||
- `HYPER_TASK_BUF`: Success, and more data was received.
|
||||
- `HYPER_TASK_ERROR`: An error retrieving the data.
|
||||
- `HYPER_TASK_EMPTY`: The body has finished streaming data.
|
||||
|
||||
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.
|
||||
*/
|
||||
struct hyper_task *hyper_body_data(struct hyper_body *body);
|
||||
|
||||
/*
|
||||
Return a task that will poll the body and execute the callback with each
|
||||
body chunk that is received.
|
||||
|
||||
The `hyper_buf` pointer is only a borrowed reference, it cannot live outside
|
||||
the execution of the callback. You must make a copy to retain it.
|
||||
|
||||
The callback should return `HYPER_ITER_CONTINUE` to continue iterating
|
||||
chunks as they are received, or `HYPER_ITER_BREAK` to cancel.
|
||||
|
||||
This will consume the `hyper_body *`, you shouldn't use it anymore or free it.
|
||||
*/
|
||||
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(struct hyper_body *body, void *userdata);
|
||||
|
||||
/*
|
||||
Set the data callback for this body.
|
||||
|
||||
The callback is called each time hyper needs to send more data for the
|
||||
body. It is passed the value from `hyper_body_set_userdata`.
|
||||
|
||||
If there is data available, the `hyper_buf **` argument should be set
|
||||
to a `hyper_buf *` containing the data, and `HYPER_POLL_READY` should
|
||||
be returned.
|
||||
|
||||
Returning `HYPER_POLL_READY` while the `hyper_buf **` argument points
|
||||
to `NULL` will indicate the body has completed all data.
|
||||
|
||||
If there is more data to send, but it isn't yet available, a
|
||||
`hyper_waker` should be saved from the `hyper_context *` argument, and
|
||||
`HYPER_POLL_PENDING` should be returned. You must wake the saved waker
|
||||
to signal the task when data is available.
|
||||
|
||||
If some error has occurred, you can return `HYPER_POLL_ERROR` to abort
|
||||
the body.
|
||||
*/
|
||||
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.
|
||||
|
||||
This makes an owned copy of the bytes, so the `buf` argument can be
|
||||
freed or changed afterwards.
|
||||
|
||||
This returns `NULL` if allocating a new buffer fails.
|
||||
*/
|
||||
struct hyper_buf *hyper_buf_copy(const uint8_t *buf, size_t len);
|
||||
|
||||
/*
|
||||
Get a pointer to the bytes in this buffer.
|
||||
|
||||
This should be used in conjunction with `hyper_buf_len` to get the length
|
||||
of the bytes data.
|
||||
|
||||
This pointer is borrowed data, and not valid once the `hyper_buf` is
|
||||
consumed/freed.
|
||||
*/
|
||||
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 struct hyper_buf *buf);
|
||||
|
||||
/*
|
||||
Free this buffer.
|
||||
*/
|
||||
void hyper_buf_free(struct hyper_buf *buf);
|
||||
|
||||
/*
|
||||
Starts an HTTP client connection handshake using the provided IO transport
|
||||
and options.
|
||||
|
||||
Both the `io` and the `options` are consumed in this function call.
|
||||
|
||||
The returned `hyper_task *` must be polled with an executor until the
|
||||
handshake completes, at which point the value can be taken.
|
||||
*/
|
||||
struct hyper_task *hyper_clientconn_handshake(struct hyper_io *io,
|
||||
struct hyper_clientconn_options *options);
|
||||
|
||||
/*
|
||||
Send a request on the client connection.
|
||||
|
||||
Returns a task that needs to be polled until it is ready. When ready, the
|
||||
task yields a `hyper_response *`.
|
||||
*/
|
||||
struct hyper_task *hyper_clientconn_send(struct hyper_clientconn *conn, struct hyper_request *req);
|
||||
|
||||
/*
|
||||
Free a `hyper_clientconn *`.
|
||||
*/
|
||||
void hyper_clientconn_free(struct hyper_clientconn *conn);
|
||||
|
||||
/*
|
||||
Creates a new set of HTTP clientconn options to be used in a handshake.
|
||||
*/
|
||||
struct hyper_clientconn_options *hyper_clientconn_options_new(void);
|
||||
|
||||
/*
|
||||
Set the whether or not header case is preserved.
|
||||
|
||||
Pass `0` to allow lowercase normalization (default), `1` to retain original case.
|
||||
*/
|
||||
void hyper_clientconn_options_set_preserve_header_case(struct hyper_clientconn_options *opts,
|
||||
int enabled);
|
||||
|
||||
/*
|
||||
Set the whether or not header order is preserved.
|
||||
|
||||
Pass `0` to allow reordering (default), `1` to retain original ordering.
|
||||
*/
|
||||
void hyper_clientconn_options_set_preserve_header_order(struct hyper_clientconn_options *opts,
|
||||
int enabled);
|
||||
|
||||
/*
|
||||
Free a `hyper_clientconn_options *`.
|
||||
*/
|
||||
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(struct hyper_clientconn_options *opts,
|
||||
const struct hyper_executor *exec);
|
||||
|
||||
/*
|
||||
Set the whether to use HTTP2.
|
||||
|
||||
Pass `0` to disable, `1` to enable.
|
||||
*/
|
||||
enum hyper_code hyper_clientconn_options_http2(struct hyper_clientconn_options *opts, int enabled);
|
||||
|
||||
/*
|
||||
Set the whether to include a copy of the raw headers in responses
|
||||
received on this connection.
|
||||
|
||||
Pass `0` to disable, `1` to enable.
|
||||
|
||||
If enabled, see `hyper_response_headers_raw()` for usage.
|
||||
*/
|
||||
enum hyper_code hyper_clientconn_options_headers_raw(struct hyper_clientconn_options *opts,
|
||||
int enabled);
|
||||
|
||||
/*
|
||||
Frees a `hyper_error`.
|
||||
*/
|
||||
void hyper_error_free(struct hyper_error *err);
|
||||
|
||||
/*
|
||||
Get an equivalent `hyper_code` from this error.
|
||||
*/
|
||||
enum hyper_code hyper_error_code(const struct hyper_error *err);
|
||||
|
||||
/*
|
||||
Print the details of this error to a buffer.
|
||||
|
||||
The `dst_len` value must be the maximum length that the buffer can
|
||||
store.
|
||||
|
||||
The return value is number of bytes that were written to `dst`.
|
||||
*/
|
||||
size_t hyper_error_print(const struct hyper_error *err, uint8_t *dst, size_t dst_len);
|
||||
|
||||
/*
|
||||
Construct a new HTTP request.
|
||||
*/
|
||||
struct hyper_request *hyper_request_new(void);
|
||||
|
||||
/*
|
||||
Free an HTTP request if not going to send it on a client.
|
||||
*/
|
||||
void hyper_request_free(struct hyper_request *req);
|
||||
|
||||
/*
|
||||
Set the HTTP Method of the request.
|
||||
*/
|
||||
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.
|
||||
|
||||
The request's URI is best described as the `request-target` from the RFCs. So in HTTP/1,
|
||||
whatever is set will get sent as-is in the first line (GET $uri HTTP/1.1). It
|
||||
supports the 4 defined variants, origin-form, absolute-form, authority-form, and
|
||||
asterisk-form.
|
||||
|
||||
The underlying type was built to efficiently support HTTP/2 where the request-target is
|
||||
split over :scheme, :authority, and :path. As such, each part can be set explicitly, or the
|
||||
type can parse a single contiguous string and if a scheme is found, that slot is "set". If
|
||||
the string just starts with a path, only the path portion is set. All pseudo headers that
|
||||
have been parsed/set are sent when the connection type is HTTP/2.
|
||||
|
||||
To set each slot explicitly, use `hyper_request_set_uri_parts`.
|
||||
*/
|
||||
enum hyper_code hyper_request_set_uri(struct hyper_request *req,
|
||||
const uint8_t *uri,
|
||||
size_t uri_len);
|
||||
|
||||
/*
|
||||
Set the URI of the request with separate scheme, authority, and
|
||||
path/query strings.
|
||||
|
||||
Each of `scheme`, `authority`, and `path_and_query` should either be
|
||||
null, to skip providing a component, or point to a UTF-8 encoded
|
||||
string. If any string pointer argument is non-null, its corresponding
|
||||
`len` parameter must be set to the string's length.
|
||||
*/
|
||||
enum hyper_code hyper_request_set_uri_parts(struct hyper_request *req,
|
||||
const uint8_t *scheme,
|
||||
size_t scheme_len,
|
||||
const uint8_t *authority,
|
||||
size_t authority_len,
|
||||
const uint8_t *path_and_query,
|
||||
size_t path_and_query_len);
|
||||
|
||||
/*
|
||||
Set the preferred HTTP version of the request.
|
||||
|
||||
The version value should be one of the `HYPER_HTTP_VERSION_` constants.
|
||||
|
||||
Note that this won't change the major HTTP version of the connection,
|
||||
since that is determined at the handshake step.
|
||||
*/
|
||||
enum hyper_code hyper_request_set_version(struct hyper_request *req, int version);
|
||||
|
||||
/*
|
||||
Gets a reference to the HTTP headers of this request
|
||||
|
||||
This is not an owned reference, so it should not be accessed after the
|
||||
`hyper_request` has been consumed.
|
||||
*/
|
||||
struct hyper_headers *hyper_request_headers(struct hyper_request *req);
|
||||
|
||||
/*
|
||||
Set the body of the request.
|
||||
|
||||
The default is an empty body.
|
||||
|
||||
This takes ownership of the `hyper_body *`, you must not use it or
|
||||
free it after setting it on the request.
|
||||
*/
|
||||
enum hyper_code hyper_request_set_body(struct hyper_request *req, struct hyper_body *body);
|
||||
|
||||
/*
|
||||
Set an informational (1xx) response callback.
|
||||
|
||||
The callback is called each time hyper receives an informational (1xx)
|
||||
response for this request.
|
||||
|
||||
The third argument is an opaque user data pointer, which is passed to
|
||||
the callback each time.
|
||||
|
||||
The callback is passed the `void *` data pointer, and a
|
||||
`hyper_response *` which can be inspected as any other response. The
|
||||
body of the response will always be empty.
|
||||
|
||||
NOTE: The `hyper_response *` is just borrowed data, and will not
|
||||
be valid after the callback finishes. You must copy any data you wish
|
||||
to persist.
|
||||
*/
|
||||
enum hyper_code hyper_request_on_informational(struct hyper_request *req,
|
||||
hyper_request_on_informational_callback callback,
|
||||
void *data);
|
||||
|
||||
/*
|
||||
Free an HTTP response after using it.
|
||||
*/
|
||||
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 struct hyper_response *resp);
|
||||
|
||||
/*
|
||||
Get a pointer to the reason-phrase of this response.
|
||||
|
||||
This buffer is not null-terminated.
|
||||
|
||||
This buffer is owned by the response, and should not be used after
|
||||
the response has been freed.
|
||||
|
||||
Use `hyper_response_reason_phrase_len()` to get the length of this
|
||||
buffer.
|
||||
*/
|
||||
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 struct hyper_response *resp);
|
||||
|
||||
/*
|
||||
Get a reference to the full raw headers of this response.
|
||||
|
||||
You must have enabled `hyper_clientconn_options_headers_raw()`, or this
|
||||
will return NULL.
|
||||
|
||||
The returned `hyper_buf *` is just a reference, owned by the response.
|
||||
You need to make a copy if you wish to use it after freeing the
|
||||
response.
|
||||
|
||||
The buffer is not null-terminated, see the `hyper_buf` functions for
|
||||
getting the bytes and length.
|
||||
*/
|
||||
const struct hyper_buf *hyper_response_headers_raw(const struct hyper_response *resp);
|
||||
|
||||
/*
|
||||
Get the HTTP version used by this response.
|
||||
|
||||
The returned value could be:
|
||||
|
||||
- `HYPER_HTTP_VERSION_1_0`
|
||||
- `HYPER_HTTP_VERSION_1_1`
|
||||
- `HYPER_HTTP_VERSION_2`
|
||||
- `HYPER_HTTP_VERSION_NONE` if newer (or older).
|
||||
*/
|
||||
int hyper_response_version(const struct hyper_response *resp);
|
||||
|
||||
/*
|
||||
Gets a reference to the HTTP headers of this response.
|
||||
|
||||
This is not an owned reference, so it should not be accessed after the
|
||||
`hyper_response` has been freed.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
struct hyper_body *hyper_response_body(struct hyper_response *resp);
|
||||
|
||||
/*
|
||||
Iterates the headers passing each name and value pair to the callback.
|
||||
|
||||
The `userdata` pointer is also passed to the callback.
|
||||
|
||||
The callback should return `HYPER_ITER_CONTINUE` to keep iterating, or
|
||||
`HYPER_ITER_BREAK` to stop.
|
||||
*/
|
||||
void hyper_headers_foreach(const struct hyper_headers *headers,
|
||||
hyper_headers_foreach_callback func,
|
||||
void *userdata);
|
||||
|
||||
/*
|
||||
Sets the header with the provided name to the provided value.
|
||||
|
||||
This overwrites any previous value set for the header.
|
||||
*/
|
||||
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.
|
||||
|
||||
If there were already existing values for the name, this will append the
|
||||
new value to the internal list.
|
||||
*/
|
||||
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.
|
||||
|
||||
The read and write functions of this transport should be set with
|
||||
`hyper_io_set_read` and `hyper_io_set_write`.
|
||||
*/
|
||||
struct hyper_io *hyper_io_new(void);
|
||||
|
||||
/*
|
||||
Free an unused `hyper_io *`.
|
||||
|
||||
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(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(struct hyper_io *io, void *data);
|
||||
|
||||
/*
|
||||
Set the read function for this IO transport.
|
||||
|
||||
Data that is read from the transport should be put in the `buf` pointer,
|
||||
up to `buf_len` bytes. The number of bytes read should be the return value.
|
||||
|
||||
It is undefined behavior to try to access the bytes in the `buf` pointer,
|
||||
unless you have already written them yourself. It is also undefined behavior
|
||||
to return that more bytes have been written than actually set on the `buf`.
|
||||
|
||||
If there is no data currently available, a waker should be claimed from
|
||||
the `ctx` and registered with whatever polling mechanism is used to signal
|
||||
when data is available later on. The return value should be
|
||||
`HYPER_IO_PENDING`.
|
||||
|
||||
If there is an irrecoverable error reading data, then `HYPER_IO_ERROR`
|
||||
should be the return value.
|
||||
*/
|
||||
void hyper_io_set_read(struct hyper_io *io, hyper_io_read_callback func);
|
||||
|
||||
/*
|
||||
Set the write function for this IO transport.
|
||||
|
||||
Data from the `buf` pointer should be written to the transport, up to
|
||||
`buf_len` bytes. The number of bytes written should be the return value.
|
||||
|
||||
If no data can currently be written, the `waker` should be cloned and
|
||||
registered with whatever polling mechanism is used to signal when data
|
||||
is available later on. The return value should be `HYPER_IO_PENDING`.
|
||||
|
||||
Yeet.
|
||||
|
||||
If there is an irrecoverable error reading data, then `HYPER_IO_ERROR`
|
||||
should be the return value.
|
||||
*/
|
||||
void hyper_io_set_write(struct hyper_io *io, hyper_io_write_callback func);
|
||||
|
||||
/*
|
||||
Creates a new task executor.
|
||||
*/
|
||||
const struct hyper_executor *hyper_executor_new(void);
|
||||
|
||||
/*
|
||||
Frees an executor and any incomplete tasks still part of it.
|
||||
*/
|
||||
void hyper_executor_free(const struct hyper_executor *exec);
|
||||
|
||||
/*
|
||||
Push a task onto the executor.
|
||||
|
||||
The executor takes ownership of the task, it should not be accessed
|
||||
again unless returned back to the user with `hyper_executor_poll`.
|
||||
*/
|
||||
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
|
||||
that they are ready again.
|
||||
|
||||
If ready, returns a task from the executor that has completed.
|
||||
|
||||
If there are no ready tasks, this returns `NULL`.
|
||||
*/
|
||||
struct hyper_task *hyper_executor_poll(const struct hyper_executor *exec);
|
||||
|
||||
/*
|
||||
Free a task.
|
||||
*/
|
||||
void hyper_task_free(struct hyper_task *task);
|
||||
|
||||
/*
|
||||
Takes the output value of this task.
|
||||
|
||||
This must only be called once polling the task on an executor has finished
|
||||
this task.
|
||||
|
||||
Use `hyper_task_type` to determine the type of the `void *` return value.
|
||||
*/
|
||||
void *hyper_task_value(struct hyper_task *task);
|
||||
|
||||
/*
|
||||
Query the return type of this task.
|
||||
*/
|
||||
enum hyper_task_return_type hyper_task_type(struct hyper_task *task);
|
||||
|
||||
/*
|
||||
Set a user data pointer to be associated with this task.
|
||||
|
||||
This value will be passed to task callbacks, and can be checked later
|
||||
with `hyper_task_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(struct hyper_task *task);
|
||||
|
||||
/*
|
||||
Copies a waker out of the task context.
|
||||
*/
|
||||
struct hyper_waker *hyper_context_waker(struct hyper_context *cx);
|
||||
|
||||
/*
|
||||
Free a waker that hasn't been woken.
|
||||
*/
|
||||
void hyper_waker_free(struct hyper_waker *waker);
|
||||
|
||||
/*
|
||||
Wake up the task associated with a waker.
|
||||
|
||||
NOTE: This consumes the waker. You should not use or free the waker afterwards.
|
||||
*/
|
||||
void hyper_waker_wake(struct hyper_waker *waker);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif /* _HYPER_H */
|
||||
Loading…
Add table
Add a link
Reference in a new issue