Skip to main content

hyper/ffi/
http_types.rs

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