Skip to content

Commit c9c46ed

Browse files
committed
refactor(ffi): Add Reason-Phrase API
This adds an internal ability to copy the HTTP/1 reason-phrase and place it in the `http::Extensions` of a response, if it doesn't match the canonical reason. This could be exposed in the Rust API later, but for now it is only used by the C API.
1 parent 4c32dae commit c9c46ed

File tree

5 files changed

+88
-4
lines changed

5 files changed

+88
-4
lines changed

capi/examples/client.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,10 @@ int main(int argc, char *argv[]) {
254254
hyper_task_free(task);
255255

256256
uint16_t http_status = hyper_response_status(resp);
257+
const uint8_t *rp = hyper_response_reason_phrase(resp);
258+
size_t rp_len = hyper_response_reason_phrase_len(resp);
257259

258-
printf("\nResponse Status: %d\n", http_status);
260+
printf("\nResponse Status: %d %.*s\n", http_status, (int) rp_len, rp);
259261

260262
hyper_headers *headers = hyper_response_headers(resp);
261263
hyper_headers_foreach(headers, print_each_header, NULL);

capi/include/hyper.h

+20
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,26 @@ void hyper_response_free(hyper_response *resp);
349349
*/
350350
uint16_t hyper_response_status(const hyper_response *resp);
351351

352+
/*
353+
Get a pointer to the reason-phrase of this response.
354+
355+
This buffer is not null-terminated.
356+
357+
This buffer is owned by the response, and should not be used after
358+
the response has been freed.
359+
360+
Use `hyper_response_reason_phrase_len()` to get the length of this
361+
buffer.
362+
*/
363+
const uint8_t *hyper_response_reason_phrase(const hyper_response *resp);
364+
365+
/*
366+
Get the length of the reason-phrase of this response.
367+
368+
Use `hyper_response_reason_phrase()` to get the buffer pointer.
369+
*/
370+
size_t hyper_response_reason_phrase_len(const hyper_response *resp);
371+
352372
/*
353373
Get the HTTP version used by this response.
354374

src/ffi/http_types.rs

+39
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ pub struct hyper_headers {
2323
#[derive(Debug, Default)]
2424
pub(crate) struct HeaderCaseMap(HeaderMap<Bytes>);
2525

26+
#[derive(Debug)]
27+
pub(crate) struct ReasonPhrase(pub(crate) Bytes);
28+
2629
// ===== impl hyper_request =====
2730

2831
ffi_fn! {
@@ -150,6 +153,30 @@ ffi_fn! {
150153
}
151154
}
152155

156+
ffi_fn! {
157+
/// Get a pointer to the reason-phrase of this response.
158+
///
159+
/// This buffer is not null-terminated.
160+
///
161+
/// This buffer is owned by the response, and should not be used after
162+
/// the response has been freed.
163+
///
164+
/// Use `hyper_response_reason_phrase_len()` to get the length of this
165+
/// buffer.
166+
fn hyper_response_reason_phrase(resp: *const hyper_response) -> *const u8 {
167+
unsafe { &*resp }.reason_phrase().as_ptr()
168+
}
169+
}
170+
171+
ffi_fn! {
172+
/// Get the length of the reason-phrase of this response.
173+
///
174+
/// Use `hyper_response_reason_phrase()` to get the buffer pointer.
175+
fn hyper_response_reason_phrase_len(resp: *const hyper_response) -> size_t {
176+
unsafe { &*resp }.reason_phrase().len()
177+
}
178+
}
179+
153180
ffi_fn! {
154181
/// Get the HTTP version used by this response.
155182
///
@@ -205,6 +232,18 @@ impl hyper_response {
205232

206233
hyper_response(resp)
207234
}
235+
236+
fn reason_phrase(&self) -> &[u8] {
237+
if let Some(reason) = self.0.extensions().get::<ReasonPhrase>() {
238+
return &reason.0;
239+
}
240+
241+
if let Some(reason) = self.0.status().canonical_reason() {
242+
return reason.as_bytes();
243+
}
244+
245+
&[]
246+
}
208247
}
209248

210249
unsafe impl AsTaskType for hyper_response {

src/ffi/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ mod io;
2828
mod task;
2929

3030
pub(crate) use self::body::UserBody;
31-
pub(crate) use self::http_types::HeaderCaseMap;
31+
pub(crate) use self::http_types::{HeaderCaseMap, ReasonPhrase};
3232

3333
pub const HYPER_ITER_CONTINUE: libc::c_int = 0;
3434
#[allow(unused)]

src/proto/h1/role.rs

+25-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use std::fmt::{self, Write};
66
use std::mem;
77

8+
#[cfg(feature = "ffi")]
9+
use bytes::Bytes;
810
use bytes::BytesMut;
911
use http::header::{self, Entry, HeaderName, HeaderValue};
1012
use http::{HeaderMap, Method, StatusCode, Version};
@@ -660,7 +662,7 @@ impl Http1Transaction for Client {
660662
loop {
661663
// Unsafe: see comment in Server Http1Transaction, above.
662664
let mut headers_indices: [HeaderIndices; MAX_HEADERS] = unsafe { mem::uninitialized() };
663-
let (len, status, version, headers_len) = {
665+
let (len, status, reason, version, headers_len) = {
664666
let mut headers: [httparse::Header<'_>; MAX_HEADERS] =
665667
unsafe { mem::uninitialized() };
666668
trace!(
@@ -674,14 +676,28 @@ impl Http1Transaction for Client {
674676
httparse::Status::Complete(len) => {
675677
trace!("Response.parse Complete({})", len);
676678
let status = StatusCode::from_u16(res.code.unwrap())?;
679+
680+
#[cfg(not(feature = "ffi"))]
681+
let reason = ();
682+
#[cfg(feature = "ffi")]
683+
let reason = {
684+
let reason = res.reason.unwrap();
685+
// Only save the reason phrase if it isnt the canonical reason
686+
if Some(reason) != status.canonical_reason() {
687+
Some(Bytes::copy_from_slice(reason.as_bytes()))
688+
} else {
689+
None
690+
}
691+
};
692+
677693
let version = if res.version.unwrap() == 1 {
678694
Version::HTTP_11
679695
} else {
680696
Version::HTTP_10
681697
};
682698
record_header_indices(bytes, &res.headers, &mut headers_indices)?;
683699
let headers_len = res.headers.len();
684-
(len, status, version, headers_len)
700+
(len, status, reason, version, headers_len)
685701
}
686702
httparse::Status::Partial => return Ok(None),
687703
}
@@ -728,6 +744,13 @@ impl Http1Transaction for Client {
728744
extensions.insert(header_case_map);
729745
}
730746

747+
#[cfg(feature = "ffi")]
748+
if let Some(reason) = reason {
749+
extensions.insert(crate::ffi::ReasonPhrase(reason));
750+
}
751+
#[cfg(not(feature = "ffi"))]
752+
drop(reason);
753+
731754
let head = MessageHead {
732755
version,
733756
subject: status,

0 commit comments

Comments
 (0)