Skip to content

Commit 25d18c0

Browse files
committedJul 12, 2021
feat(ffi): add hyper_request_on_informational
This defines an extension type used in requests for the client that is used to setup a callback for receipt of informational (1xx) responses. The type isn't currently public, and is only usable in the C API.
1 parent 1cd40b7 commit 25d18c0

File tree

10 files changed

+162
-8
lines changed

10 files changed

+162
-8
lines changed
 

‎capi/examples/upload.c

+15-2
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,16 @@ static int print_each_header(void *userdata,
148148
return HYPER_ITER_CONTINUE;
149149
}
150150

151+
static void print_informational(void *userdata, hyper_response *resp) {
152+
uint16_t http_status = hyper_response_status(resp);
153+
154+
printf("\nInformational (1xx): %d\n", http_status);
155+
156+
hyper_headers *headers = hyper_response_headers(resp);
157+
hyper_headers_foreach(headers, print_each_header, NULL);
158+
printf("\n");
159+
}
160+
151161
typedef enum {
152162
EXAMPLE_NOT_SET = 0, // tasks we don't know about won't have a userdata set
153163
EXAMPLE_HANDSHAKE,
@@ -172,7 +182,7 @@ int main(int argc, char *argv[]) {
172182
upload.fd = open(file, O_RDONLY);
173183

174184
if (upload.fd < 0) {
175-
printf("error opening file to upload: %d", errno);
185+
printf("error opening file to upload: %s\n", strerror(errno));
176186
return 1;
177187
}
178188
printf("connecting to port %s on %s...\n", port, host);
@@ -262,7 +272,10 @@ int main(int argc, char *argv[]) {
262272
}
263273

264274
hyper_headers *req_headers = hyper_request_headers(req);
265-
hyper_headers_set(req_headers, STR_ARG("host"), STR_ARG(host));
275+
hyper_headers_set(req_headers, STR_ARG("host"), STR_ARG(host));
276+
hyper_headers_set(req_headers, STR_ARG("expect"), STR_ARG("100-continue"));
277+
278+
hyper_request_on_informational(req, print_informational, NULL);
266279

267280
// Prepare the req body
268281
hyper_body *body = hyper_body_new();

‎capi/include/hyper.h

+23
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ typedef int (*hyper_body_foreach_callback)(void*, const struct hyper_buf*);
207207

208208
typedef int (*hyper_body_data_callback)(void*, struct hyper_context*, struct hyper_buf**);
209209

210+
typedef void (*hyper_request_on_informational_callback)(void*, const struct hyper_response*);
211+
210212
typedef int (*hyper_headers_foreach_callback)(void*, const uint8_t*, size_t, const uint8_t*, size_t);
211213

212214
typedef size_t (*hyper_io_read_callback)(void*, struct hyper_context*, uint8_t*, size_t);
@@ -454,6 +456,27 @@ struct hyper_headers *hyper_request_headers(struct hyper_request *req);
454456
*/
455457
enum hyper_code hyper_request_set_body(struct hyper_request *req, struct hyper_body *body);
456458

459+
/*
460+
Set an informational (1xx) response callback.
461+
462+
The callback is called each time hyper receives an informational (1xx)
463+
response for this request.
464+
465+
The third argument is an opaque user data pointer, which is passed to
466+
the callback each time.
467+
468+
The callback is passed the `void *` data pointer, and a
469+
`hyper_response *` which can be inspected as any other response. The
470+
body of the response will always be empty.
471+
472+
NOTE: The `const hyper_response *` is just borrowed data, and will not
473+
be valid after the callback finishes. You must copy any data you wish
474+
to persist.
475+
*/
476+
enum hyper_code hyper_request_on_informational(struct hyper_request *req,
477+
hyper_request_on_informational_callback callback,
478+
void *data);
479+
457480
/*
458481
Free an HTTP response after using it.
459482
*/

‎src/ffi/http_types.rs

+43-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::ffi::c_void;
55
use super::body::{hyper_body, hyper_buf};
66
use super::error::hyper_code;
77
use super::task::{hyper_task_return_type, AsTaskType};
8-
use super::HYPER_ITER_CONTINUE;
8+
use super::{UserDataPointer, HYPER_ITER_CONTINUE};
99
use crate::ext::HeaderCaseMap;
1010
use crate::header::{HeaderName, HeaderValue};
1111
use crate::{Body, HeaderMap, Method, Request, Response, Uri};
@@ -29,6 +29,13 @@ pub(crate) struct ReasonPhrase(pub(crate) Bytes);
2929

3030
pub(crate) struct RawHeaders(pub(crate) hyper_buf);
3131

32+
pub(crate) struct OnInformational {
33+
func: hyper_request_on_informational_callback,
34+
data: UserDataPointer,
35+
}
36+
37+
type hyper_request_on_informational_callback = extern "C" fn(*mut c_void, *const hyper_response);
38+
3239
// ===== impl hyper_request =====
3340

3441
ffi_fn! {
@@ -129,6 +136,32 @@ ffi_fn! {
129136
}
130137
}
131138

139+
ffi_fn! {
140+
/// Set an informational (1xx) response callback.
141+
///
142+
/// The callback is called each time hyper receives an informational (1xx)
143+
/// response for this request.
144+
///
145+
/// The third argument is an opaque user data pointer, which is passed to
146+
/// the callback each time.
147+
///
148+
/// The callback is passed the `void *` data pointer, and a
149+
/// `hyper_response *` which can be inspected as any other response. The
150+
/// body of the response will always be empty.
151+
///
152+
/// NOTE: The `const hyper_response *` is just borrowed data, and will not
153+
/// be valid after the callback finishes. You must copy any data you wish
154+
/// to persist.
155+
fn hyper_request_on_informational(req: *mut hyper_request, callback: hyper_request_on_informational_callback, data: *mut c_void) -> hyper_code {
156+
let ext = OnInformational {
157+
func: callback,
158+
data: UserDataPointer(data),
159+
};
160+
unsafe { &mut *req }.0.extensions_mut().insert(ext);
161+
hyper_code::HYPERE_OK
162+
}
163+
}
164+
132165
impl hyper_request {
133166
pub(super) fn finalize_request(&mut self) {
134167
if let Some(headers) = self.0.extensions_mut().remove::<hyper_headers>() {
@@ -394,6 +427,15 @@ unsafe fn raw_name_value(
394427
Ok((name, value, orig_name))
395428
}
396429

430+
// ===== impl OnInformational =====
431+
432+
impl OnInformational {
433+
pub(crate) fn call(&mut self, resp: Response<Body>) {
434+
let mut resp = hyper_response(resp);
435+
(self.func)(self.data.0, &mut resp);
436+
}
437+
}
438+
397439
#[cfg(test)]
398440
mod tests {
399441
use super::*;

‎src/ffi/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ struct UserDataPointer(*mut std::ffi::c_void);
8181
// We don't actually know anything about this pointer, it's up to the user
8282
// to do the right thing.
8383
unsafe impl Send for UserDataPointer {}
84+
unsafe impl Sync for UserDataPointer {}
8485

8586
/// cbindgen:ignore
8687
static VERSION_CSTR: &str = concat!(env!("CARGO_PKG_VERSION"), "\0");

‎src/proto/h1/conn.rs

+23
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ where
5050
title_case_headers: false,
5151
h09_responses: false,
5252
#[cfg(feature = "ffi")]
53+
on_informational: None,
54+
#[cfg(feature = "ffi")]
5355
raw_headers: false,
5456
notify_read: false,
5557
reading: Reading::Init,
@@ -170,6 +172,8 @@ where
170172
preserve_header_case: self.state.preserve_header_case,
171173
h09_responses: self.state.h09_responses,
172174
#[cfg(feature = "ffi")]
175+
on_informational: &mut self.state.on_informational,
176+
#[cfg(feature = "ffi")]
173177
raw_headers: self.state.raw_headers,
174178
}
175179
)) {
@@ -185,6 +189,12 @@ where
185189
// Prevent accepting HTTP/0.9 responses after the initial one, if any.
186190
self.state.h09_responses = false;
187191

192+
// Drop any OnInformational callbacks, we're done there!
193+
#[cfg(feature = "ffi")]
194+
{
195+
self.state.on_informational = None;
196+
}
197+
188198
self.state.busy();
189199
self.state.keep_alive &= msg.keep_alive;
190200
self.state.version = msg.head.version;
@@ -525,6 +535,14 @@ where
525535
debug_assert!(self.state.cached_headers.is_none());
526536
debug_assert!(head.headers.is_empty());
527537
self.state.cached_headers = Some(head.headers);
538+
539+
#[cfg(feature = "ffi")]
540+
{
541+
self.state.on_informational = head
542+
.extensions
543+
.remove::<crate::ffi::OnInformational>();
544+
}
545+
528546
Some(encoder)
529547
}
530548
Err(err) => {
@@ -775,6 +793,11 @@ struct State {
775793
preserve_header_case: bool,
776794
title_case_headers: bool,
777795
h09_responses: bool,
796+
/// If set, called with each 1xx informational response received for
797+
/// the current request. MUST be unset after a non-1xx response is
798+
/// received.
799+
#[cfg(feature = "ffi")]
800+
on_informational: Option<crate::ffi::OnInformational>,
778801
#[cfg(feature = "ffi")]
779802
raw_headers: bool,
780803
/// Set to true when the Dispatcher should poll read operations

‎src/proto/h1/dispatch.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -598,11 +598,7 @@ cfg_client! {
598598
match msg {
599599
Ok((msg, body)) => {
600600
if let Some(cb) = self.callback.take() {
601-
let mut res = http::Response::new(body);
602-
*res.status_mut() = msg.subject;
603-
*res.headers_mut() = msg.headers;
604-
*res.version_mut() = msg.version;
605-
*res.extensions_mut() = msg.extensions;
601+
let res = msg.into_response(body);
606602
cb.send(Ok(res));
607603
Ok(())
608604
} else {

‎src/proto/h1/io.rs

+4
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ where
168168
preserve_header_case: parse_ctx.preserve_header_case,
169169
h09_responses: parse_ctx.h09_responses,
170170
#[cfg(feature = "ffi")]
171+
on_informational: parse_ctx.on_informational,
172+
#[cfg(feature = "ffi")]
171173
raw_headers: parse_ctx.raw_headers,
172174
},
173175
)? {
@@ -678,6 +680,8 @@ mod tests {
678680
preserve_header_case: false,
679681
h09_responses: false,
680682
#[cfg(feature = "ffi")]
683+
on_informational: &mut None,
684+
#[cfg(feature = "ffi")]
681685
raw_headers: false,
682686
};
683687
assert!(buffered

‎src/proto/h1/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ pub(crate) struct ParseContext<'a> {
7575
preserve_header_case: bool,
7676
h09_responses: bool,
7777
#[cfg(feature = "ffi")]
78+
on_informational: &'a mut Option<crate::ffi::OnInformational>,
79+
#[cfg(feature = "ffi")]
7880
raw_headers: bool,
7981
}
8082

‎src/proto/h1/role.rs

+39
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,13 @@ impl Http1Transaction for Client {
991991
}));
992992
}
993993

994+
#[cfg(feature = "ffi")]
995+
if head.subject.is_informational() {
996+
if let Some(callback) = ctx.on_informational {
997+
callback.call(head.into_response(crate::Body::empty()));
998+
}
999+
}
1000+
9941001
// Parsing a 1xx response could have consumed the buffer, check if
9951002
// it is empty now...
9961003
if buf.is_empty() {
@@ -1428,6 +1435,8 @@ mod tests {
14281435
preserve_header_case: false,
14291436
h09_responses: false,
14301437
#[cfg(feature = "ffi")]
1438+
on_informational: &mut None,
1439+
#[cfg(feature = "ffi")]
14311440
raw_headers: false,
14321441
},
14331442
)
@@ -1453,6 +1462,8 @@ mod tests {
14531462
preserve_header_case: false,
14541463
h09_responses: false,
14551464
#[cfg(feature = "ffi")]
1465+
on_informational: &mut None,
1466+
#[cfg(feature = "ffi")]
14561467
raw_headers: false,
14571468
};
14581469
let msg = Client::parse(&mut raw, ctx).unwrap().unwrap();
@@ -1473,6 +1484,8 @@ mod tests {
14731484
preserve_header_case: false,
14741485
h09_responses: false,
14751486
#[cfg(feature = "ffi")]
1487+
on_informational: &mut None,
1488+
#[cfg(feature = "ffi")]
14761489
raw_headers: false,
14771490
};
14781491
Server::parse(&mut raw, ctx).unwrap_err();
@@ -1491,6 +1504,8 @@ mod tests {
14911504
preserve_header_case: false,
14921505
h09_responses: true,
14931506
#[cfg(feature = "ffi")]
1507+
on_informational: &mut None,
1508+
#[cfg(feature = "ffi")]
14941509
raw_headers: false,
14951510
};
14961511
let msg = Client::parse(&mut raw, ctx).unwrap().unwrap();
@@ -1511,6 +1526,8 @@ mod tests {
15111526
preserve_header_case: false,
15121527
h09_responses: false,
15131528
#[cfg(feature = "ffi")]
1529+
on_informational: &mut None,
1530+
#[cfg(feature = "ffi")]
15141531
raw_headers: false,
15151532
};
15161533
Client::parse(&mut raw, ctx).unwrap_err();
@@ -1535,6 +1552,8 @@ mod tests {
15351552
preserve_header_case: false,
15361553
h09_responses: false,
15371554
#[cfg(feature = "ffi")]
1555+
on_informational: &mut None,
1556+
#[cfg(feature = "ffi")]
15381557
raw_headers: false,
15391558
};
15401559
let msg = Client::parse(&mut raw, ctx).unwrap().unwrap();
@@ -1556,6 +1575,8 @@ mod tests {
15561575
preserve_header_case: false,
15571576
h09_responses: false,
15581577
#[cfg(feature = "ffi")]
1578+
on_informational: &mut None,
1579+
#[cfg(feature = "ffi")]
15591580
raw_headers: false,
15601581
};
15611582
Client::parse(&mut raw, ctx).unwrap_err();
@@ -1572,6 +1593,8 @@ mod tests {
15721593
preserve_header_case: true,
15731594
h09_responses: false,
15741595
#[cfg(feature = "ffi")]
1596+
on_informational: &mut None,
1597+
#[cfg(feature = "ffi")]
15751598
raw_headers: false,
15761599
};
15771600
let parsed_message = Server::parse(&mut raw, ctx).unwrap().unwrap();
@@ -1609,6 +1632,8 @@ mod tests {
16091632
preserve_header_case: false,
16101633
h09_responses: false,
16111634
#[cfg(feature = "ffi")]
1635+
on_informational: &mut None,
1636+
#[cfg(feature = "ffi")]
16121637
raw_headers: false,
16131638
},
16141639
)
@@ -1627,6 +1652,8 @@ mod tests {
16271652
preserve_header_case: false,
16281653
h09_responses: false,
16291654
#[cfg(feature = "ffi")]
1655+
on_informational: &mut None,
1656+
#[cfg(feature = "ffi")]
16301657
raw_headers: false,
16311658
},
16321659
)
@@ -1854,6 +1881,8 @@ mod tests {
18541881
preserve_header_case: false,
18551882
h09_responses: false,
18561883
#[cfg(feature = "ffi")]
1884+
on_informational: &mut None,
1885+
#[cfg(feature = "ffi")]
18571886
raw_headers: false,
18581887
}
18591888
)
@@ -1872,6 +1901,8 @@ mod tests {
18721901
preserve_header_case: false,
18731902
h09_responses: false,
18741903
#[cfg(feature = "ffi")]
1904+
on_informational: &mut None,
1905+
#[cfg(feature = "ffi")]
18751906
raw_headers: false,
18761907
},
18771908
)
@@ -1890,6 +1921,8 @@ mod tests {
18901921
preserve_header_case: false,
18911922
h09_responses: false,
18921923
#[cfg(feature = "ffi")]
1924+
on_informational: &mut None,
1925+
#[cfg(feature = "ffi")]
18931926
raw_headers: false,
18941927
},
18951928
)
@@ -2383,6 +2416,8 @@ mod tests {
23832416
preserve_header_case: false,
23842417
h09_responses: false,
23852418
#[cfg(feature = "ffi")]
2419+
on_informational: &mut None,
2420+
#[cfg(feature = "ffi")]
23862421
raw_headers: false,
23872422
},
23882423
)
@@ -2465,6 +2500,8 @@ mod tests {
24652500
preserve_header_case: false,
24662501
h09_responses: false,
24672502
#[cfg(feature = "ffi")]
2503+
on_informational: &mut None,
2504+
#[cfg(feature = "ffi")]
24682505
raw_headers: false,
24692506
},
24702507
)
@@ -2503,6 +2540,8 @@ mod tests {
25032540
preserve_header_case: false,
25042541
h09_responses: false,
25052542
#[cfg(feature = "ffi")]
2543+
on_informational: &mut None,
2544+
#[cfg(feature = "ffi")]
25062545
raw_headers: false,
25072546
},
25082547
)

‎src/proto/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,14 @@ pub(crate) enum Dispatched {
5757
#[cfg(feature = "http1")]
5858
Upgrade(crate::upgrade::Pending),
5959
}
60+
61+
impl MessageHead<http::StatusCode> {
62+
fn into_response<B>(self, body: B) -> http::Response<B> {
63+
let mut res = http::Response::new(body);
64+
*res.status_mut() = self.subject;
65+
*res.headers_mut() = self.headers;
66+
*res.version_mut() = self.version;
67+
*res.extensions_mut() = self.extensions;
68+
res
69+
}
70+
}

0 commit comments

Comments
 (0)
Please sign in to comment.