Skip to content

Commit

Permalink
Add C API Rust FFI code
Browse files Browse the repository at this point in the history
  • Loading branch information
seanmonstar committed Sep 19, 2020
1 parent d35a40a commit 8753a87
Show file tree
Hide file tree
Showing 11 changed files with 369 additions and 5 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/target
/Cargo.lock
target
Cargo.lock
22 changes: 22 additions & 0 deletions hyper-capi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "hyper-capi"
version = "0.0.0"
description = "A fast and correct HTTP library."
readme = "README.md"
homepage = "https://hyper.rs"
documentation = "https://github.com/hyperium/hyper/tree/master/hyper-capi"
repository = "https://github.com/hyperium/hyper"
license = "MIT"
authors = ["Sean McArthur <sean@seanmonstar.com>"]
edition = "2018"

publish = false

[lib]
name = "hyper_c"
crate-type = ["staticlib", "cdylib"]

[dependencies]
libc = "0.2"
hyper = { version = "0.13", path = "..", default-features = false }
tokio = "0.2"
5 changes: 4 additions & 1 deletion hyper-capi/examples/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include <netdb.h>
#include <string.h>

#include "../include/hyper.h"
#include "hyper.h"


struct conn_data {
Expand Down Expand Up @@ -112,12 +112,15 @@ static hyper_iter_step print_each_header(void *userdata, hyper_str name, hyper_s
}

int main(int argc, char *argv[]) {
printf("connecting ...");

int fd = connect_to("httpbin.org", "80");
if (fd < 0) {
return 1;
}

printf("connected to httpbin.org");


struct conn_fds *all_fds = malloc(sizeof(struct conn_fds));

Expand Down
2 changes: 0 additions & 2 deletions hyper-capi/include/hyper.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ extern "C" {
#include <stdint.h>
#include <stddef.h>

#define TODO void

typedef struct hyper_clientconn hyper_clientconn;

typedef struct hyper_clientconn_options hyper_clientconn_options;
Expand Down
1 change: 1 addition & 0 deletions hyper-capi/src/body.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use crate::hyper_str;
31 changes: 31 additions & 0 deletions hyper-capi/src/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use hyper::client::conn;
use hyper::{Body, Request};

use crate::io::Io;
use crate::task::Task;

pub struct Options {
builder: conn::Builder,
}

pub struct ClientConn {
tx: conn::SendRequest<hyper::Body>,
}

ffi_fn! {
fn hyper_clientconn_handshake(io: *mut Io, options: *mut Options) -> *mut Task {
let options = unsafe { Box::from_raw(options) };
let io = unsafe { Box::from_raw(io) };

Box::into_raw(Task::boxed(options.builder.handshake::<_, hyper::Body>(io)))
}
}

ffi_fn! {
fn hyper_clientconn_send(conn: *mut ClientConn, req: *mut Request<Body>) -> *mut Task {
let req = unsafe { Box::from_raw(req) };
let fut = unsafe { &mut *conn }.tx.send_request(*req);

Box::into_raw(Task::boxed(fut))
}
}
110 changes: 110 additions & 0 deletions hyper-capi/src/http_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use hyper::{Body, HeaderMap, Method, Request, Response, Uri};
use libc::size_t;
use std::ffi::c_void;

use crate::{hyper_error, hyper_str};

// ===== impl Request =====

ffi_fn! {
fn hyper_request_new() -> *mut Request<Body> {
Box::into_raw(Box::new(Request::new(Body::empty())))
}
}

ffi_fn! {
fn hyper_request_free(req: *mut Request<Body>) {
drop(unsafe { Box::from_raw(req) });
}
}

ffi_fn! {
fn hyper_request_set_method(req: *mut Request<Body>, method: *const u8, method_len: size_t) -> hyper_error {
let bytes = unsafe {
std::slice::from_raw_parts(method, method_len as usize)
};
match Method::from_bytes(bytes) {
Ok(m) => {
*unsafe { &mut *req }.method_mut() = m;
hyper_error::Ok
},
Err(_) => {
hyper_error::Kaboom
}
}
}
}

ffi_fn! {
fn hyper_request_set_uri(req: *mut Request<Body>, uri: *const u8, uri_len: size_t) -> hyper_error {
let bytes = unsafe {
std::slice::from_raw_parts(uri, uri_len as usize)
};
match Uri::from_maybe_shared(bytes) {
Ok(u) => {
*unsafe { &mut *req }.uri_mut() = u;
hyper_error::Ok
},
Err(_) => {
hyper_error::Kaboom
}
}
}
}

// ===== impl Response =====

ffi_fn! {
fn hyper_response_free(resp: *mut Response<Body>) {
drop(unsafe { Box::from_raw(resp) });
}
}

ffi_fn! {
fn hyper_response_status(resp: *const Response<Body>) -> u16 {
unsafe { &*resp }.status().as_u16()
}
}

ffi_fn! {
fn hyper_response_headers(resp: *mut Response<Body>) -> *mut HeaderMap {
unsafe { &mut *resp }.headers_mut()
}
}

ffi_fn! {
fn hyper_response_body(resp: *mut Response<Body>) -> *mut Body {
unsafe { &mut *resp }.body_mut()
}
}

// ===== impl Headers =====

#[repr(C)]
#[derive(PartialEq)]
pub enum IterStep {
Continue = 0,
#[allow(unused)]
Break,
}

type IterFn = extern "C" fn(*mut c_void, hyper_str, hyper_str) -> IterStep;

ffi_fn! {
fn hyper_headers_iter(headers: *const HeaderMap, func: IterFn, userdata: *mut c_void) {
for (name, value) in unsafe { &*headers }.iter() {
let raw_name = hyper_str {
buf: name.as_str().as_bytes().as_ptr(),
len: name.as_str().as_bytes().len(),
};
let raw_val = hyper_str {
buf: value.as_bytes().as_ptr(),
len: value.as_bytes().len(),
};

if IterStep::Continue != func(userdata, raw_name, raw_val) {
break;
}
}
}
}
94 changes: 94 additions & 0 deletions hyper-capi/src/io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use std::pin::Pin;
use std::task::{Context, Poll};

use libc::size_t;
use tokio::io::{AsyncRead, AsyncWrite};

use crate::task::hyper_waker;

type ReadFn = extern "C" fn(*mut (), *const hyper_waker, *mut u8, size_t) -> size_t;
type WriteFn = extern "C" fn(*mut (), *const hyper_waker, *const u8, size_t) -> size_t;

/// `typedef struct hyper_io hyper_io`
pub struct Io {
read: ReadFn,
write: WriteFn,
userdata: *mut (),
}

ffi_fn! {
fn hyper_io_new() -> *mut Io {
Box::into_raw(Box::new(Io {
read: read_noop,
write: write_noop,
userdata: std::ptr::null_mut(),
}))
}
}

ffi_fn! {
fn hyper_io_set_data(io: *mut Io, data: *mut ()) {
unsafe { &mut *io }.userdata = data;
}
}

ffi_fn! {
fn hyper_io_set_read(io: *mut Io, func: ReadFn) {
unsafe { &mut *io }.read = func;
}
}

ffi_fn! {
fn hyper_io_set_write(io: *mut Io, func: WriteFn) {
unsafe { &mut *io }.write = func;
}
}

extern "C" fn read_noop(
_userdata: *mut (),
_: *const hyper_waker,
_buf: *mut u8,
_buf_len: size_t,
) -> size_t {
0
}

extern "C" fn write_noop(
_userdata: *mut (),
_: *const hyper_waker,
_buf: *const u8,
_buf_len: size_t,
) -> size_t {
0
}

impl AsyncRead for Io {
fn poll_read(
self: Pin<&mut Self>,
_: &mut Context<'_>,
_buf: &mut [u8],
) -> Poll<std::io::Result<usize>> {
todo!("poll_read");
}
}

impl AsyncWrite for Io {
fn poll_write(
self: Pin<&mut Self>,
_: &mut Context<'_>,
_buf: &[u8],
) -> Poll<std::io::Result<usize>> {
todo!("poll_write");
}

fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Poll::Ready(Ok(()))
}

fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Poll::Ready(Ok(()))
}
}

unsafe impl Send for Io {}
unsafe impl Sync for Io {}
20 changes: 20 additions & 0 deletions hyper-capi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#[macro_use]
mod macros;

mod body;
mod client;
mod http_types;
mod io;
mod task;

#[repr(C)]
pub struct hyper_str {
pub buf: *const u8,
pub len: libc::size_t,
}

#[repr(C)]
pub enum hyper_error {
Ok = 0,
Kaboom = 1,
}
17 changes: 17 additions & 0 deletions hyper-capi/src/macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
macro_rules! ffi_fn {
(fn $name:ident($($arg:ident: $arg_ty:ty),*) -> $ret:ty $body:block) => {
#[no_mangle]
pub extern fn $name($($arg: $arg_ty),*) -> $ret {
use std::panic::{self, AssertUnwindSafe};

match panic::catch_unwind(AssertUnwindSafe(move || $body)) {
Ok(v) => v,
Err(_) => todo!("FFI UNWIND"),
}
}
};

(fn $name:ident($($arg:ident: $arg_ty:ty),*) $body:block) => {
ffi_fn!(fn $name($($arg: $arg_ty),*) -> () $body);
};
}

0 comments on commit 8753a87

Please sign in to comment.