rama_hyper/ffi/
http_types.rs

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