rustrust-warp

How can two headers of the same name be attached to a Warp `Reply`?


I'd like to write a function that returns impl Reply, i.e. a Warp handler. This function does some business logic and then should return two Set-Cookie headers; the contents of each cookie is different and dependent on the business logic. I had been using a pattern like this:

async fn my_handler() -> anyhow::Result<impl Reply> {
    // Some business logic...

    let reply = warp::reply::json(&json!({}));
    let reply = warp::reply::with_status(reply, StatusCode::OK);
    let reply = warp::reply::with_header(
        reply,
        header::SET_COOKIE,
        "foo=bar",
    );

    Ok(warp::reply::with_header(
        reply,
        header::SET_COOKIE,
        "baz=qux",
    ))
}

However this will result in only the second cookie being set. Separately there's warp::filters::reply::headers, which initially seemed to be what I want but it's unclear how this can play nice with reply above.


Solution

  • I was able to work around this by converting the reply into a Response and then manually manipulating the response. This is similar to cperez08's answer, but allows for two headers of the same name to be attached to the response:

    async fn my_handler() -> anyhow::Result<impl Reply> {
        // Some business logic...
    
        let reply = warp::reply::json(&json!({}));
        let reply = warp::reply::with_status(reply, StatusCode::OK);
    
        // Set multiple e.g. cookies.
        let mut cookies = HeaderMap::new();
        cookies.append(header::SET_COOKIE, HeaderValue::from_str("foo").unwrap());
        cookies.append(header::SET_COOKIE, HeaderValue::from_str("bar").unwrap());
    
        // Convert `reply` into a `Response` so we can extend headers.
        let mut response = reply.into_response();
        let headers = response.headers_mut();
        headers.extend(cookies);
    
        Ok(response)
    }