Skip to main content

folk_plugin_http/
payload.rs

1//! HTTP payload encoding: Hyper ↔ MessagePack.
2//!
3//! Request to PHP:  msgpack map { method, uri, headers, body }
4//! Response from PHP: msgpack map { status, headers, body }
5
6use std::collections::HashMap;
7
8use anyhow::{Context, Result};
9use bytes::Bytes;
10use serde::{Deserialize, Serialize};
11
12/// Serialized HTTP request sent to PHP worker.
13#[derive(Debug, Serialize, Deserialize)]
14pub struct HttpRequestPayload {
15    pub method: String,
16    pub uri: String,
17    pub headers: HashMap<String, String>,
18    pub body: Bytes,
19}
20
21/// Deserialized HTTP response from PHP worker.
22#[derive(Debug, Serialize, Deserialize)]
23pub struct HttpResponsePayload {
24    pub status: u16,
25    pub headers: HashMap<String, String>,
26    pub body: Bytes,
27}
28
29/// Encode a hyper Request to MessagePack bytes.
30pub async fn encode_request<B>(req: hyper::Request<B>) -> Result<Bytes>
31where
32    B: hyper::body::Body<Data = Bytes> + Send,
33    B::Error: std::error::Error + Send + Sync + 'static,
34{
35    let (parts, body) = req.into_parts();
36    let body_bytes = http_body_util::BodyExt::collect(body)
37        .await
38        .context("read request body")?
39        .to_bytes();
40
41    let headers: HashMap<String, String> = parts
42        .headers
43        .iter()
44        .filter_map(|(k, v)| Some((k.to_string(), v.to_str().ok()?.to_string())))
45        .collect();
46
47    let payload = HttpRequestPayload {
48        method: parts.method.to_string(),
49        uri: parts.uri.to_string(),
50        headers,
51        body: body_bytes,
52    };
53
54    let encoded = rmp_serde::to_vec_named(&payload).context("encode request")?;
55    Ok(Bytes::from(encoded))
56}
57
58/// Decode MessagePack bytes to hyper Response.
59pub fn decode_response(bytes: Bytes) -> Result<hyper::Response<http_body_util::Full<Bytes>>> {
60    let payload: HttpResponsePayload = rmp_serde::from_slice(&bytes).context("decode response")?;
61
62    let mut builder = hyper::Response::builder().status(payload.status);
63    for (k, v) in &payload.headers {
64        builder = builder.header(k.as_str(), v.as_str());
65    }
66
67    builder
68        .body(http_body_util::Full::new(payload.body))
69        .context("build response")
70}