Skip to content

Commit 8753a87

Browse files
committed
Add C API Rust FFI code
1 parent d35a40a commit 8753a87

File tree

11 files changed

+369
-5
lines changed

11 files changed

+369
-5
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
/target
2-
/Cargo.lock
1+
target
2+
Cargo.lock

hyper-capi/Cargo.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
name = "hyper-capi"
3+
version = "0.0.0"
4+
description = "A fast and correct HTTP library."
5+
readme = "README.md"
6+
homepage = "https://hyper.rs"
7+
documentation = "https://github.com/hyperium/hyper/tree/master/hyper-capi"
8+
repository = "https://github.com/hyperium/hyper"
9+
license = "MIT"
10+
authors = ["Sean McArthur <[email protected]>"]
11+
edition = "2018"
12+
13+
publish = false
14+
15+
[lib]
16+
name = "hyper_c"
17+
crate-type = ["staticlib", "cdylib"]
18+
19+
[dependencies]
20+
libc = "0.2"
21+
hyper = { version = "0.13", path = "..", default-features = false }
22+
tokio = "0.2"

hyper-capi/examples/client.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#include <netdb.h>
1010
#include <string.h>
1111

12-
#include "../include/hyper.h"
12+
#include "hyper.h"
1313

1414

1515
struct conn_data {
@@ -112,12 +112,15 @@ static hyper_iter_step print_each_header(void *userdata, hyper_str name, hyper_s
112112
}
113113

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

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

122+
printf("connected to httpbin.org");
123+
121124

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

hyper-capi/include/hyper.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ extern "C" {
88
#include <stdint.h>
99
#include <stddef.h>
1010

11-
#define TODO void
12-
1311
typedef struct hyper_clientconn hyper_clientconn;
1412

1513
typedef struct hyper_clientconn_options hyper_clientconn_options;

hyper-capi/src/body.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
use crate::hyper_str;

hyper-capi/src/client.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use hyper::client::conn;
2+
use hyper::{Body, Request};
3+
4+
use crate::io::Io;
5+
use crate::task::Task;
6+
7+
pub struct Options {
8+
builder: conn::Builder,
9+
}
10+
11+
pub struct ClientConn {
12+
tx: conn::SendRequest<hyper::Body>,
13+
}
14+
15+
ffi_fn! {
16+
fn hyper_clientconn_handshake(io: *mut Io, options: *mut Options) -> *mut Task {
17+
let options = unsafe { Box::from_raw(options) };
18+
let io = unsafe { Box::from_raw(io) };
19+
20+
Box::into_raw(Task::boxed(options.builder.handshake::<_, hyper::Body>(io)))
21+
}
22+
}
23+
24+
ffi_fn! {
25+
fn hyper_clientconn_send(conn: *mut ClientConn, req: *mut Request<Body>) -> *mut Task {
26+
let req = unsafe { Box::from_raw(req) };
27+
let fut = unsafe { &mut *conn }.tx.send_request(*req);
28+
29+
Box::into_raw(Task::boxed(fut))
30+
}
31+
}

hyper-capi/src/http_types.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
use hyper::{Body, HeaderMap, Method, Request, Response, Uri};
2+
use libc::size_t;
3+
use std::ffi::c_void;
4+
5+
use crate::{hyper_error, hyper_str};
6+
7+
// ===== impl Request =====
8+
9+
ffi_fn! {
10+
fn hyper_request_new() -> *mut Request<Body> {
11+
Box::into_raw(Box::new(Request::new(Body::empty())))
12+
}
13+
}
14+
15+
ffi_fn! {
16+
fn hyper_request_free(req: *mut Request<Body>) {
17+
drop(unsafe { Box::from_raw(req) });
18+
}
19+
}
20+
21+
ffi_fn! {
22+
fn hyper_request_set_method(req: *mut Request<Body>, method: *const u8, method_len: size_t) -> hyper_error {
23+
let bytes = unsafe {
24+
std::slice::from_raw_parts(method, method_len as usize)
25+
};
26+
match Method::from_bytes(bytes) {
27+
Ok(m) => {
28+
*unsafe { &mut *req }.method_mut() = m;
29+
hyper_error::Ok
30+
},
31+
Err(_) => {
32+
hyper_error::Kaboom
33+
}
34+
}
35+
}
36+
}
37+
38+
ffi_fn! {
39+
fn hyper_request_set_uri(req: *mut Request<Body>, uri: *const u8, uri_len: size_t) -> hyper_error {
40+
let bytes = unsafe {
41+
std::slice::from_raw_parts(uri, uri_len as usize)
42+
};
43+
match Uri::from_maybe_shared(bytes) {
44+
Ok(u) => {
45+
*unsafe { &mut *req }.uri_mut() = u;
46+
hyper_error::Ok
47+
},
48+
Err(_) => {
49+
hyper_error::Kaboom
50+
}
51+
}
52+
}
53+
}
54+
55+
// ===== impl Response =====
56+
57+
ffi_fn! {
58+
fn hyper_response_free(resp: *mut Response<Body>) {
59+
drop(unsafe { Box::from_raw(resp) });
60+
}
61+
}
62+
63+
ffi_fn! {
64+
fn hyper_response_status(resp: *const Response<Body>) -> u16 {
65+
unsafe { &*resp }.status().as_u16()
66+
}
67+
}
68+
69+
ffi_fn! {
70+
fn hyper_response_headers(resp: *mut Response<Body>) -> *mut HeaderMap {
71+
unsafe { &mut *resp }.headers_mut()
72+
}
73+
}
74+
75+
ffi_fn! {
76+
fn hyper_response_body(resp: *mut Response<Body>) -> *mut Body {
77+
unsafe { &mut *resp }.body_mut()
78+
}
79+
}
80+
81+
// ===== impl Headers =====
82+
83+
#[repr(C)]
84+
#[derive(PartialEq)]
85+
pub enum IterStep {
86+
Continue = 0,
87+
#[allow(unused)]
88+
Break,
89+
}
90+
91+
type IterFn = extern "C" fn(*mut c_void, hyper_str, hyper_str) -> IterStep;
92+
93+
ffi_fn! {
94+
fn hyper_headers_iter(headers: *const HeaderMap, func: IterFn, userdata: *mut c_void) {
95+
for (name, value) in unsafe { &*headers }.iter() {
96+
let raw_name = hyper_str {
97+
buf: name.as_str().as_bytes().as_ptr(),
98+
len: name.as_str().as_bytes().len(),
99+
};
100+
let raw_val = hyper_str {
101+
buf: value.as_bytes().as_ptr(),
102+
len: value.as_bytes().len(),
103+
};
104+
105+
if IterStep::Continue != func(userdata, raw_name, raw_val) {
106+
break;
107+
}
108+
}
109+
}
110+
}

hyper-capi/src/io.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use std::pin::Pin;
2+
use std::task::{Context, Poll};
3+
4+
use libc::size_t;
5+
use tokio::io::{AsyncRead, AsyncWrite};
6+
7+
use crate::task::hyper_waker;
8+
9+
type ReadFn = extern "C" fn(*mut (), *const hyper_waker, *mut u8, size_t) -> size_t;
10+
type WriteFn = extern "C" fn(*mut (), *const hyper_waker, *const u8, size_t) -> size_t;
11+
12+
/// `typedef struct hyper_io hyper_io`
13+
pub struct Io {
14+
read: ReadFn,
15+
write: WriteFn,
16+
userdata: *mut (),
17+
}
18+
19+
ffi_fn! {
20+
fn hyper_io_new() -> *mut Io {
21+
Box::into_raw(Box::new(Io {
22+
read: read_noop,
23+
write: write_noop,
24+
userdata: std::ptr::null_mut(),
25+
}))
26+
}
27+
}
28+
29+
ffi_fn! {
30+
fn hyper_io_set_data(io: *mut Io, data: *mut ()) {
31+
unsafe { &mut *io }.userdata = data;
32+
}
33+
}
34+
35+
ffi_fn! {
36+
fn hyper_io_set_read(io: *mut Io, func: ReadFn) {
37+
unsafe { &mut *io }.read = func;
38+
}
39+
}
40+
41+
ffi_fn! {
42+
fn hyper_io_set_write(io: *mut Io, func: WriteFn) {
43+
unsafe { &mut *io }.write = func;
44+
}
45+
}
46+
47+
extern "C" fn read_noop(
48+
_userdata: *mut (),
49+
_: *const hyper_waker,
50+
_buf: *mut u8,
51+
_buf_len: size_t,
52+
) -> size_t {
53+
0
54+
}
55+
56+
extern "C" fn write_noop(
57+
_userdata: *mut (),
58+
_: *const hyper_waker,
59+
_buf: *const u8,
60+
_buf_len: size_t,
61+
) -> size_t {
62+
0
63+
}
64+
65+
impl AsyncRead for Io {
66+
fn poll_read(
67+
self: Pin<&mut Self>,
68+
_: &mut Context<'_>,
69+
_buf: &mut [u8],
70+
) -> Poll<std::io::Result<usize>> {
71+
todo!("poll_read");
72+
}
73+
}
74+
75+
impl AsyncWrite for Io {
76+
fn poll_write(
77+
self: Pin<&mut Self>,
78+
_: &mut Context<'_>,
79+
_buf: &[u8],
80+
) -> Poll<std::io::Result<usize>> {
81+
todo!("poll_write");
82+
}
83+
84+
fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<std::io::Result<()>> {
85+
Poll::Ready(Ok(()))
86+
}
87+
88+
fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<std::io::Result<()>> {
89+
Poll::Ready(Ok(()))
90+
}
91+
}
92+
93+
unsafe impl Send for Io {}
94+
unsafe impl Sync for Io {}

hyper-capi/src/lib.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#[macro_use]
2+
mod macros;
3+
4+
mod body;
5+
mod client;
6+
mod http_types;
7+
mod io;
8+
mod task;
9+
10+
#[repr(C)]
11+
pub struct hyper_str {
12+
pub buf: *const u8,
13+
pub len: libc::size_t,
14+
}
15+
16+
#[repr(C)]
17+
pub enum hyper_error {
18+
Ok = 0,
19+
Kaboom = 1,
20+
}

hyper-capi/src/macros.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
macro_rules! ffi_fn {
2+
(fn $name:ident($($arg:ident: $arg_ty:ty),*) -> $ret:ty $body:block) => {
3+
#[no_mangle]
4+
pub extern fn $name($($arg: $arg_ty),*) -> $ret {
5+
use std::panic::{self, AssertUnwindSafe};
6+
7+
match panic::catch_unwind(AssertUnwindSafe(move || $body)) {
8+
Ok(v) => v,
9+
Err(_) => todo!("FFI UNWIND"),
10+
}
11+
}
12+
};
13+
14+
(fn $name:ident($($arg:ident: $arg_ty:ty),*) $body:block) => {
15+
ffi_fn!(fn $name($($arg: $arg_ty),*) -> () $body);
16+
};
17+
}

0 commit comments

Comments
 (0)