Skip to content

Commit 20fa29a

Browse files
committed
feat: allow setting http response header in fullstack
1 parent 0aa7351 commit 20fa29a

File tree

3 files changed

+38
-5
lines changed

3 files changed

+38
-5
lines changed

packages/fullstack-core/src/streaming.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use crate::{HttpError, ServerFnError};
22
use axum_core::{extract::FromRequest, response::IntoResponse};
33
use dioxus_core::{try_consume_context, CapturedError};
44
use dioxus_signals::{ReadableExt, Signal, WritableExt};
5-
use http::request::Parts;
65
use http::StatusCode;
6+
use http::{request::Parts, HeaderMap};
77
use std::{cell::RefCell, rc::Rc};
88

99
/// The context provided by dioxus fullstack for server-side rendering.
@@ -13,6 +13,7 @@ use std::{cell::RefCell, rc::Rc};
1313
pub struct FullstackContext {
1414
current_status: Signal<StreamingStatus>,
1515
request_headers: Rc<RefCell<http::request::Parts>>,
16+
response_headers: Rc<RefCell<Option<HeaderMap>>>,
1617
route_http_status: Signal<HttpError>,
1718
}
1819

@@ -34,6 +35,7 @@ impl FullstackContext {
3435
status: http::StatusCode::OK,
3536
message: None,
3637
}),
38+
response_headers: Rc::new(RefCell::new(Some(HeaderMap::new()))),
3739
}
3840
}
3941

@@ -99,6 +101,23 @@ impl FullstackContext {
99101
self.route_http_status.set(status);
100102
}
101103

104+
/// Add a header to the response. This will be sent to the client when the response is committed.
105+
pub fn add_response_header(
106+
&self,
107+
key: impl Into<http::header::HeaderName>,
108+
value: impl Into<http::header::HeaderValue>,
109+
) {
110+
if let Some(headers) = self.response_headers.borrow_mut().as_mut() {
111+
headers.insert(key.into(), value.into());
112+
}
113+
}
114+
115+
/// Take the response headers out of the context. This will leave the context without any headers,
116+
/// so it should only be called once when the response is being committed.
117+
pub fn take_response_headers(&self) -> Option<HeaderMap> {
118+
self.response_headers.borrow_mut().take()
119+
}
120+
102121
/// Set the current HTTP status for the route. This will be used when committing the response
103122
/// to the client.
104123
pub fn commit_http_status(status: StatusCode, message: Option<String>) {

packages/fullstack-server/src/server.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,15 +304,23 @@ impl RenderHandleState {
304304
.await;
305305

306306
match response {
307-
Ok((status, freshness, rx)) => {
307+
Ok((status, headers, freshness, rx)) => {
308308
let mut response = Response::builder()
309309
.status(status.status)
310310
.header(CONTENT_TYPE, "text/html; charset=utf-8")
311311
.body(Body::from_stream(rx))
312312
.unwrap();
313313

314+
// Write our freshness header
314315
freshness.write(response.headers_mut());
315316

317+
// write the other headers set by the user
318+
for (key, value) in headers.into_iter() {
319+
if let Some(key) = key {
320+
response.headers_mut().insert(key, value);
321+
}
322+
}
323+
316324
response
317325
}
318326

packages/fullstack-server/src/ssr.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use dioxus_router::ParseRouteError;
1717
use dioxus_ssr::Renderer;
1818
use futures_channel::mpsc::Sender;
1919
use futures_util::{Stream, StreamExt};
20-
use http::{request::Parts, StatusCode};
20+
use http::{request::Parts, HeaderMap, StatusCode};
2121
use std::{
2222
collections::HashMap,
2323
fmt::Write,
@@ -103,6 +103,7 @@ impl SsrRendererPool {
103103
) -> Result<
104104
(
105105
HttpError,
106+
HeaderMap,
106107
RenderFreshness,
107108
impl Stream<Item = Result<String, IncrementalRendererError>>,
108109
),
@@ -154,6 +155,7 @@ impl SsrRendererPool {
154155
status: StatusCode::OK,
155156
message: None,
156157
},
158+
HeaderMap::new(),
157159
freshness,
158160
ReceiverWithDrop {
159161
receiver: rx,
@@ -292,9 +294,12 @@ impl SsrRendererPool {
292294

293295
// Check the FullstackContext in case the user set the statuscode manually or via a layout.
294296
let http_status = streaming_context.current_http_status();
297+
let headers = streaming_context
298+
.take_response_headers()
299+
.unwrap_or_default();
295300

296301
// Now that we handled any errors from rendering, we can send the initial ok result
297-
_ = initial_result_tx.send(Ok(http_status));
302+
_ = initial_result_tx.send(Ok((http_status, headers)));
298303

299304
// Wait long enough to assemble the `<head>` of the document before starting to stream
300305
let mut pre_body = String::new();
@@ -418,12 +423,13 @@ impl SsrRendererPool {
418423
let join_handle = Self::spawn_platform(create_render_future);
419424

420425
// Wait for the initial result which determines the status code
421-
let status = initial_result_rx
426+
let (status, headers) = initial_result_rx
422427
.await
423428
.map_err(|err| SSRError::Incremental(IncrementalRendererError::Other(err.into())))??;
424429

425430
Ok((
426431
status,
432+
headers,
427433
RenderFreshness::now(None),
428434
ReceiverWithDrop {
429435
receiver: rx,

0 commit comments

Comments
 (0)