1 use bytes::Bytes;
2 use libc::{c_int, size_t};
3 use std::ffi::c_void;
4
5 use super::body::{hyper_body, hyper_buf};
6 use super::error::hyper_code;
7 use super::task::{hyper_task_return_type, AsTaskType};
8 use super::{UserDataPointer, HYPER_ITER_CONTINUE};
9 use crate::ext::{HeaderCaseMap, OriginalHeaderOrder, ReasonPhrase};
10 use crate::header::{HeaderName, HeaderValue};
11 use crate::{Body, HeaderMap, Method, Request, Response, Uri};
12
13 /// An HTTP request.
14 pub struct hyper_request(pub(super) Request<Body>);
15
16 /// An HTTP response.
17 pub struct hyper_response(pub(super) Response<Body>);
18
19 /// An HTTP header map.
20 ///
21 /// These can be part of a request or response.
22 pub struct hyper_headers {
23 pub(super) headers: HeaderMap,
24 orig_casing: HeaderCaseMap,
25 orig_order: OriginalHeaderOrder,
26 }
27
28 pub(crate) struct RawHeaders(pub(crate) hyper_buf);
29
30 pub(crate) struct OnInformational {
31 func: hyper_request_on_informational_callback,
32 data: UserDataPointer,
33 }
34
35 type hyper_request_on_informational_callback = extern "C" fn(*mut c_void, *mut hyper_response);
36
37 // ===== impl hyper_request =====
38
39 ffi_fn! {
40 /// Construct a new HTTP request.
41 fn hyper_request_new() -> *mut hyper_request {
42 Box::into_raw(Box::new(hyper_request(Request::new(Body::empty()))))
43 } ?= std::ptr::null_mut()
44 }
45
46 ffi_fn! {
47 /// Free an HTTP request if not going to send it on a client.
48 fn hyper_request_free(req: *mut hyper_request) {
49 drop(non_null!(Box::from_raw(req) ?= ()));
50 }
51 }
52
53 ffi_fn! {
54 /// Set the HTTP Method of the request.
55 fn hyper_request_set_method(req: *mut hyper_request, method: *const u8, method_len: size_t) -> hyper_code {
56 let bytes = unsafe {
57 std::slice::from_raw_parts(method, method_len as usize)
58 };
59 let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
60 match Method::from_bytes(bytes) {
61 Ok(m) => {
62 *req.0.method_mut() = m;
63 hyper_code::HYPERE_OK
64 },
65 Err(_) => {
66 hyper_code::HYPERE_INVALID_ARG
67 }
68 }
69 }
70 }
71
72 ffi_fn! {
73 /// Set the URI of the request.
74 ///
75 /// The request's URI is best described as the `request-target` from the RFCs. So in HTTP/1,
76 /// whatever is set will get sent as-is in the first line (GET $uri HTTP/1.1). It
77 /// supports the 4 defined variants, origin-form, absolute-form, authority-form, and
78 /// asterisk-form.
79 ///
80 /// The underlying type was built to efficiently support HTTP/2 where the request-target is
81 /// split over :scheme, :authority, and :path. As such, each part can be set explicitly, or the
82 /// type can parse a single contiguous string and if a scheme is found, that slot is "set". If
83 /// the string just starts with a path, only the path portion is set. All pseudo headers that
84 /// have been parsed/set are sent when the connection type is HTTP/2.
85 ///
86 /// To set each slot explicitly, use `hyper_request_set_uri_parts`.
87 fn hyper_request_set_uri(req: *mut hyper_request, uri: *const u8, uri_len: size_t) -> hyper_code {
88 let bytes = unsafe {
89 std::slice::from_raw_parts(uri, uri_len as usize)
90 };
91 let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
92 match Uri::from_maybe_shared(bytes) {
93 Ok(u) => {
94 *req.0.uri_mut() = u;
95 hyper_code::HYPERE_OK
96 },
97 Err(_) => {
98 hyper_code::HYPERE_INVALID_ARG
99 }
100 }
101 }
102 }
103
104 ffi_fn! {
105 /// Set the URI of the request with separate scheme, authority, and
106 /// path/query strings.
107 ///
108 /// Each of `scheme`, `authority`, and `path_and_query` should either be
109 /// null, to skip providing a component, or point to a UTF-8 encoded
110 /// string. If any string pointer argument is non-null, its corresponding
111 /// `len` parameter must be set to the string's length.
112 fn hyper_request_set_uri_parts(
113 req: *mut hyper_request,
114 scheme: *const u8,
115 scheme_len: size_t,
116 authority: *const u8,
117 authority_len: size_t,
118 path_and_query: *const u8,
119 path_and_query_len: size_t
120 ) -> hyper_code {
121 let mut builder = Uri::builder();
122 if !scheme.is_null() {
123 let scheme_bytes = unsafe {
124 std::slice::from_raw_parts(scheme, scheme_len as usize)
125 };
126 builder = builder.scheme(scheme_bytes);
127 }
128 if !authority.is_null() {
129 let authority_bytes = unsafe {
130 std::slice::from_raw_parts(authority, authority_len as usize)
131 };
132 builder = builder.authority(authority_bytes);
133 }
134 if !path_and_query.is_null() {
135 let path_and_query_bytes = unsafe {
136 std::slice::from_raw_parts(path_and_query, path_and_query_len as usize)
137 };
138 builder = builder.path_and_query(path_and_query_bytes);
139 }
140 match builder.build() {
141 Ok(u) => {
142 *unsafe { &mut *req }.0.uri_mut() = u;
143 hyper_code::HYPERE_OK
144 },
145 Err(_) => {
146 hyper_code::HYPERE_INVALID_ARG
147 }
148 }
149 }
150 }
151
152 ffi_fn! {
153 /// Set the preferred HTTP version of the request.
154 ///
155 /// The version value should be one of the `HYPER_HTTP_VERSION_` constants.
156 ///
157 /// Note that this won't change the major HTTP version of the connection,
158 /// since that is determined at the handshake step.
159 fn hyper_request_set_version(req: *mut hyper_request, version: c_int) -> hyper_code {
160 use http::Version;
161
162 let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
163 *req.0.version_mut() = match version {
164 super::HYPER_HTTP_VERSION_NONE => Version::HTTP_11,
165 super::HYPER_HTTP_VERSION_1_0 => Version::HTTP_10,
166 super::HYPER_HTTP_VERSION_1_1 => Version::HTTP_11,
167 super::HYPER_HTTP_VERSION_2 => Version::HTTP_2,
168 _ => {
169 // We don't know this version
170 return hyper_code::HYPERE_INVALID_ARG;
171 }
172 };
173 hyper_code::HYPERE_OK
174 }
175 }
176
177 ffi_fn! {
178 /// Gets a reference to the HTTP headers of this request
179 ///
180 /// This is not an owned reference, so it should not be accessed after the
181 /// `hyper_request` has been consumed.
182 fn hyper_request_headers(req: *mut hyper_request) -> *mut hyper_headers {
183 hyper_headers::get_or_default(unsafe { &mut *req }.0.extensions_mut())
184 } ?= std::ptr::null_mut()
185 }
186
187 ffi_fn! {
188 /// Set the body of the request.
189 ///
190 /// The default is an empty body.
191 ///
192 /// This takes ownership of the `hyper_body *`, you must not use it or
193 /// free it after setting it on the request.
194 fn hyper_request_set_body(req: *mut hyper_request, body: *mut hyper_body) -> hyper_code {
195 let body = non_null!(Box::from_raw(body) ?= hyper_code::HYPERE_INVALID_ARG);
196 let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
197 *req.0.body_mut() = body.0;
198 hyper_code::HYPERE_OK
199 }
200 }
201
202 ffi_fn! {
203 /// Set an informational (1xx) response callback.
204 ///
205 /// The callback is called each time hyper receives an informational (1xx)
206 /// response for this request.
207 ///
208 /// The third argument is an opaque user data pointer, which is passed to
209 /// the callback each time.
210 ///
211 /// The callback is passed the `void *` data pointer, and a
212 /// `hyper_response *` which can be inspected as any other response. The
213 /// body of the response will always be empty.
214 ///
215 /// NOTE: The `hyper_response *` is just borrowed data, and will not
216 /// be valid after the callback finishes. You must copy any data you wish
217 /// to persist.
218 fn hyper_request_on_informational(req: *mut hyper_request, callback: hyper_request_on_informational_callback, data: *mut c_void) -> hyper_code {
219 let ext = OnInformational {
220 func: callback,
221 data: UserDataPointer(data),
222 };
223 let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
224 req.0.extensions_mut().insert(ext);
225 hyper_code::HYPERE_OK
226 }
227 }
228
229 impl hyper_request {
finalize_request(&mut self)230 pub(super) fn finalize_request(&mut self) {
231 if let Some(headers) = self.0.extensions_mut().remove::<hyper_headers>() {
232 *self.0.headers_mut() = headers.headers;
233 self.0.extensions_mut().insert(headers.orig_casing);
234 self.0.extensions_mut().insert(headers.orig_order);
235 }
236 }
237 }
238
239 // ===== impl hyper_response =====
240
241 ffi_fn! {
242 /// Free an HTTP response after using it.
243 fn hyper_response_free(resp: *mut hyper_response) {
244 drop(non_null!(Box::from_raw(resp) ?= ()));
245 }
246 }
247
248 ffi_fn! {
249 /// Get the HTTP-Status code of this response.
250 ///
251 /// It will always be within the range of 100-599.
252 fn hyper_response_status(resp: *const hyper_response) -> u16 {
253 non_null!(&*resp ?= 0).0.status().as_u16()
254 }
255 }
256
257 ffi_fn! {
258 /// Get a pointer to the reason-phrase of this response.
259 ///
260 /// This buffer is not null-terminated.
261 ///
262 /// This buffer is owned by the response, and should not be used after
263 /// the response has been freed.
264 ///
265 /// Use `hyper_response_reason_phrase_len()` to get the length of this
266 /// buffer.
267 fn hyper_response_reason_phrase(resp: *const hyper_response) -> *const u8 {
268 non_null!(&*resp ?= std::ptr::null()).reason_phrase().as_ptr()
269 } ?= std::ptr::null()
270 }
271
272 ffi_fn! {
273 /// Get the length of the reason-phrase of this response.
274 ///
275 /// Use `hyper_response_reason_phrase()` to get the buffer pointer.
276 fn hyper_response_reason_phrase_len(resp: *const hyper_response) -> size_t {
277 non_null!(&*resp ?= 0).reason_phrase().len()
278 }
279 }
280
281 ffi_fn! {
282 /// Get a reference to the full raw headers of this response.
283 ///
284 /// You must have enabled `hyper_clientconn_options_headers_raw()`, or this
285 /// will return NULL.
286 ///
287 /// The returned `hyper_buf *` is just a reference, owned by the response.
288 /// You need to make a copy if you wish to use it after freeing the
289 /// response.
290 ///
291 /// The buffer is not null-terminated, see the `hyper_buf` functions for
292 /// getting the bytes and length.
293 fn hyper_response_headers_raw(resp: *const hyper_response) -> *const hyper_buf {
294 let resp = non_null!(&*resp ?= std::ptr::null());
295 match resp.0.extensions().get::<RawHeaders>() {
296 Some(raw) => &raw.0,
297 None => std::ptr::null(),
298 }
299 } ?= std::ptr::null()
300 }
301
302 ffi_fn! {
303 /// Get the HTTP version used by this response.
304 ///
305 /// The returned value could be:
306 ///
307 /// - `HYPER_HTTP_VERSION_1_0`
308 /// - `HYPER_HTTP_VERSION_1_1`
309 /// - `HYPER_HTTP_VERSION_2`
310 /// - `HYPER_HTTP_VERSION_NONE` if newer (or older).
311 fn hyper_response_version(resp: *const hyper_response) -> c_int {
312 use http::Version;
313
314 match non_null!(&*resp ?= 0).0.version() {
315 Version::HTTP_10 => super::HYPER_HTTP_VERSION_1_0,
316 Version::HTTP_11 => super::HYPER_HTTP_VERSION_1_1,
317 Version::HTTP_2 => super::HYPER_HTTP_VERSION_2,
318 _ => super::HYPER_HTTP_VERSION_NONE,
319 }
320 }
321 }
322
323 ffi_fn! {
324 /// Gets a reference to the HTTP headers of this response.
325 ///
326 /// This is not an owned reference, so it should not be accessed after the
327 /// `hyper_response` has been freed.
328 fn hyper_response_headers(resp: *mut hyper_response) -> *mut hyper_headers {
329 hyper_headers::get_or_default(unsafe { &mut *resp }.0.extensions_mut())
330 } ?= std::ptr::null_mut()
331 }
332
333 ffi_fn! {
334 /// Take ownership of the body of this response.
335 ///
336 /// It is safe to free the response even after taking ownership of its body.
337 fn hyper_response_body(resp: *mut hyper_response) -> *mut hyper_body {
338 let body = std::mem::take(non_null!(&mut *resp ?= std::ptr::null_mut()).0.body_mut());
339 Box::into_raw(Box::new(hyper_body(body)))
340 } ?= std::ptr::null_mut()
341 }
342
343 impl hyper_response {
wrap(mut resp: Response<Body>) -> hyper_response344 pub(super) fn wrap(mut resp: Response<Body>) -> hyper_response {
345 let headers = std::mem::take(resp.headers_mut());
346 let orig_casing = resp
347 .extensions_mut()
348 .remove::<HeaderCaseMap>()
349 .unwrap_or_else(HeaderCaseMap::default);
350 let orig_order = resp
351 .extensions_mut()
352 .remove::<OriginalHeaderOrder>()
353 .unwrap_or_else(OriginalHeaderOrder::default);
354 resp.extensions_mut().insert(hyper_headers {
355 headers,
356 orig_casing,
357 orig_order,
358 });
359
360 hyper_response(resp)
361 }
362
reason_phrase(&self) -> &[u8]363 fn reason_phrase(&self) -> &[u8] {
364 if let Some(reason) = self.0.extensions().get::<ReasonPhrase>() {
365 return reason.as_bytes();
366 }
367
368 if let Some(reason) = self.0.status().canonical_reason() {
369 return reason.as_bytes();
370 }
371
372 &[]
373 }
374 }
375
376 unsafe impl AsTaskType for hyper_response {
as_task_type(&self) -> hyper_task_return_type377 fn as_task_type(&self) -> hyper_task_return_type {
378 hyper_task_return_type::HYPER_TASK_RESPONSE
379 }
380 }
381
382 // ===== impl Headers =====
383
384 type hyper_headers_foreach_callback =
385 extern "C" fn(*mut c_void, *const u8, size_t, *const u8, size_t) -> c_int;
386
387 impl hyper_headers {
get_or_default(ext: &mut http::Extensions) -> &mut hyper_headers388 pub(super) fn get_or_default(ext: &mut http::Extensions) -> &mut hyper_headers {
389 if let None = ext.get_mut::<hyper_headers>() {
390 ext.insert(hyper_headers::default());
391 }
392
393 ext.get_mut::<hyper_headers>().unwrap()
394 }
395 }
396
397 ffi_fn! {
398 /// Iterates the headers passing each name and value pair to the callback.
399 ///
400 /// The `userdata` pointer is also passed to the callback.
401 ///
402 /// The callback should return `HYPER_ITER_CONTINUE` to keep iterating, or
403 /// `HYPER_ITER_BREAK` to stop.
404 fn hyper_headers_foreach(headers: *const hyper_headers, func: hyper_headers_foreach_callback, userdata: *mut c_void) {
405 let headers = non_null!(&*headers ?= ());
406 // For each header name/value pair, there may be a value in the casemap
407 // that corresponds to the HeaderValue. So, we iterator all the keys,
408 // and for each one, try to pair the originally cased name with the value.
409 //
410 // TODO: consider adding http::HeaderMap::entries() iterator
411 let mut ordered_iter = headers.orig_order.get_in_order().peekable();
412 if ordered_iter.peek().is_some() {
413 for (name, idx) in ordered_iter {
414 let (name_ptr, name_len) = if let Some(orig_name) = headers.orig_casing.get_all(name).nth(*idx) {
415 (orig_name.as_ref().as_ptr(), orig_name.as_ref().len())
416 } else {
417 (
418 name.as_str().as_bytes().as_ptr(),
419 name.as_str().as_bytes().len(),
420 )
421 };
422
423 let val_ptr;
424 let val_len;
425 if let Some(value) = headers.headers.get_all(name).iter().nth(*idx) {
426 val_ptr = value.as_bytes().as_ptr();
427 val_len = value.as_bytes().len();
428 } else {
429 // Stop iterating, something has gone wrong.
430 return;
431 }
432
433 if HYPER_ITER_CONTINUE != func(userdata, name_ptr, name_len, val_ptr, val_len) {
434 return;
435 }
436 }
437 } else {
438 for name in headers.headers.keys() {
439 let mut names = headers.orig_casing.get_all(name);
440
441 for value in headers.headers.get_all(name) {
442 let (name_ptr, name_len) = if let Some(orig_name) = names.next() {
443 (orig_name.as_ref().as_ptr(), orig_name.as_ref().len())
444 } else {
445 (
446 name.as_str().as_bytes().as_ptr(),
447 name.as_str().as_bytes().len(),
448 )
449 };
450
451 let val_ptr = value.as_bytes().as_ptr();
452 let val_len = value.as_bytes().len();
453
454 if HYPER_ITER_CONTINUE != func(userdata, name_ptr, name_len, val_ptr, val_len) {
455 return;
456 }
457 }
458 }
459 }
460 }
461 }
462
463 ffi_fn! {
464 /// Sets the header with the provided name to the provided value.
465 ///
466 /// This overwrites any previous value set for the header.
467 fn hyper_headers_set(headers: *mut hyper_headers, name: *const u8, name_len: size_t, value: *const u8, value_len: size_t) -> hyper_code {
468 let headers = non_null!(&mut *headers ?= hyper_code::HYPERE_INVALID_ARG);
469 match unsafe { raw_name_value(name, name_len, value, value_len) } {
470 Ok((name, value, orig_name)) => {
471 headers.headers.insert(&name, value);
472 headers.orig_casing.insert(name.clone(), orig_name.clone());
473 headers.orig_order.insert(name);
474 hyper_code::HYPERE_OK
475 }
476 Err(code) => code,
477 }
478 }
479 }
480
481 ffi_fn! {
482 /// Adds the provided value to the list of the provided name.
483 ///
484 /// If there were already existing values for the name, this will append the
485 /// new value to the internal list.
486 fn hyper_headers_add(headers: *mut hyper_headers, name: *const u8, name_len: size_t, value: *const u8, value_len: size_t) -> hyper_code {
487 let headers = non_null!(&mut *headers ?= hyper_code::HYPERE_INVALID_ARG);
488
489 match unsafe { raw_name_value(name, name_len, value, value_len) } {
490 Ok((name, value, orig_name)) => {
491 headers.headers.append(&name, value);
492 headers.orig_casing.append(&name, orig_name.clone());
493 headers.orig_order.append(name);
494 hyper_code::HYPERE_OK
495 }
496 Err(code) => code,
497 }
498 }
499 }
500
501 impl Default for hyper_headers {
default() -> Self502 fn default() -> Self {
503 Self {
504 headers: Default::default(),
505 orig_casing: HeaderCaseMap::default(),
506 orig_order: OriginalHeaderOrder::default(),
507 }
508 }
509 }
510
raw_name_value( name: *const u8, name_len: size_t, value: *const u8, value_len: size_t, ) -> Result<(HeaderName, HeaderValue, Bytes), hyper_code>511 unsafe fn raw_name_value(
512 name: *const u8,
513 name_len: size_t,
514 value: *const u8,
515 value_len: size_t,
516 ) -> Result<(HeaderName, HeaderValue, Bytes), hyper_code> {
517 let name = std::slice::from_raw_parts(name, name_len);
518 let orig_name = Bytes::copy_from_slice(name);
519 let name = match HeaderName::from_bytes(name) {
520 Ok(name) => name,
521 Err(_) => return Err(hyper_code::HYPERE_INVALID_ARG),
522 };
523 let value = std::slice::from_raw_parts(value, value_len);
524 let value = match HeaderValue::from_bytes(value) {
525 Ok(val) => val,
526 Err(_) => return Err(hyper_code::HYPERE_INVALID_ARG),
527 };
528
529 Ok((name, value, orig_name))
530 }
531
532 // ===== impl OnInformational =====
533
534 impl OnInformational {
call(&mut self, resp: Response<Body>)535 pub(crate) fn call(&mut self, resp: Response<Body>) {
536 let mut resp = hyper_response::wrap(resp);
537 (self.func)(self.data.0, &mut resp);
538 }
539 }
540
541 #[cfg(test)]
542 mod tests {
543 use super::*;
544
545 #[test]
test_headers_foreach_cases_preserved()546 fn test_headers_foreach_cases_preserved() {
547 let mut headers = hyper_headers::default();
548
549 let name1 = b"Set-CookiE";
550 let value1 = b"a=b";
551 hyper_headers_add(
552 &mut headers,
553 name1.as_ptr(),
554 name1.len(),
555 value1.as_ptr(),
556 value1.len(),
557 );
558
559 let name2 = b"SET-COOKIE";
560 let value2 = b"c=d";
561 hyper_headers_add(
562 &mut headers,
563 name2.as_ptr(),
564 name2.len(),
565 value2.as_ptr(),
566 value2.len(),
567 );
568
569 let mut vec = Vec::<u8>::new();
570 hyper_headers_foreach(&headers, concat, &mut vec as *mut _ as *mut c_void);
571
572 assert_eq!(vec, b"Set-CookiE: a=b\r\nSET-COOKIE: c=d\r\n");
573
574 extern "C" fn concat(
575 vec: *mut c_void,
576 name: *const u8,
577 name_len: usize,
578 value: *const u8,
579 value_len: usize,
580 ) -> c_int {
581 unsafe {
582 let vec = &mut *(vec as *mut Vec<u8>);
583 let name = std::slice::from_raw_parts(name, name_len);
584 let value = std::slice::from_raw_parts(value, value_len);
585 vec.extend(name);
586 vec.extend(b": ");
587 vec.extend(value);
588 vec.extend(b"\r\n");
589 }
590 HYPER_ITER_CONTINUE
591 }
592 }
593
594 #[cfg(all(feature = "http1", feature = "ffi"))]
595 #[test]
test_headers_foreach_order_preserved()596 fn test_headers_foreach_order_preserved() {
597 let mut headers = hyper_headers::default();
598
599 let name1 = b"Set-CookiE";
600 let value1 = b"a=b";
601 hyper_headers_add(
602 &mut headers,
603 name1.as_ptr(),
604 name1.len(),
605 value1.as_ptr(),
606 value1.len(),
607 );
608
609 let name2 = b"Content-Encoding";
610 let value2 = b"gzip";
611 hyper_headers_add(
612 &mut headers,
613 name2.as_ptr(),
614 name2.len(),
615 value2.as_ptr(),
616 value2.len(),
617 );
618
619 let name3 = b"SET-COOKIE";
620 let value3 = b"c=d";
621 hyper_headers_add(
622 &mut headers,
623 name3.as_ptr(),
624 name3.len(),
625 value3.as_ptr(),
626 value3.len(),
627 );
628
629 let mut vec = Vec::<u8>::new();
630 hyper_headers_foreach(&headers, concat, &mut vec as *mut _ as *mut c_void);
631
632 println!("{}", std::str::from_utf8(&vec).unwrap());
633 assert_eq!(
634 vec,
635 b"Set-CookiE: a=b\r\nContent-Encoding: gzip\r\nSET-COOKIE: c=d\r\n"
636 );
637
638 extern "C" fn concat(
639 vec: *mut c_void,
640 name: *const u8,
641 name_len: usize,
642 value: *const u8,
643 value_len: usize,
644 ) -> c_int {
645 unsafe {
646 let vec = &mut *(vec as *mut Vec<u8>);
647 let name = std::slice::from_raw_parts(name, name_len);
648 let value = std::slice::from_raw_parts(value, value_len);
649 vec.extend(name);
650 vec.extend(b": ");
651 vec.extend(value);
652 vec.extend(b"\r\n");
653 }
654 HYPER_ITER_CONTINUE
655 }
656 }
657 }
658