Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions src/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,14 @@ pub fn try_rsgi_sync_short_circuit(
if (method == "GET" || method == "HEAD") && path == "/openapi.json" && snapshot.include_openapi
{
let _ = protocol_py.setattr(py, "__oxyroute_path_template__", "/openapi.json");
let doc = state.read().openapi.lock().to_string();
let doc: Arc<String> = {
let state_guard = state.read();
let mut oa = state_guard.openapi.lock();
if oa.1.is_none() {
oa.1 = Some(Arc::new(oa.0.to_string()));
}
Arc::clone(oa.1.as_ref().unwrap())
};
if is_head {
response::send_head_simple_sync(
py,
Expand Down Expand Up @@ -484,7 +491,14 @@ pub async fn run_rsgi(
let _ = Python::with_gil(|py| {
protocol.setattr(py, "__oxyroute_path_template__", "/openapi.json")
});
let doc = state.read().openapi.lock().to_string();
let doc: Arc<String> = {
let state_guard = state.read();
let mut oa = state_guard.openapi.lock();
if oa.1.is_none() {
oa.1 = Some(Arc::new(oa.0.to_string()));
}
Arc::clone(oa.1.as_ref().unwrap())
};
if is_head {
return response::send_head_simple(
&protocol,
Expand Down
19 changes: 12 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,8 @@ impl App {
};
{
let mut oa = st.openapi.lock();
App::openapi_add_path(&mut oa, &method, &path, &op_id, request_schema);
App::openapi_add_path(&mut oa.0, &method, &path, &op_id, request_schema);
oa.1 = None;
}
{
let mut m = state::map_method_router(&st, &method).ok_or_else(|| {
Expand Down Expand Up @@ -349,12 +350,13 @@ impl App {
fn set_openapi_title(&self, title: &str) -> PyResult<()> {
let st = self.state.read();
let mut oa = st.openapi.lock();
if let Some(info) = oa
.as_object_mut()
.and_then(|m| m.get_mut("info"))
.and_then(|i| i.as_object_mut())
if let Some(info) =
oa.0.as_object_mut()
.and_then(|m| m.get_mut("info"))
.and_then(|i| i.as_object_mut())
{
info.insert("title".to_string(), json!(title));
oa.1 = None;
}
Ok(())
}
Expand Down Expand Up @@ -481,8 +483,11 @@ impl App {

fn openapi_json(&self) -> PyResult<String> {
let st = self.state.read();
let oa = st.openapi.lock();
Ok(oa.to_string())
let mut oa = st.openapi.lock();
if oa.1.is_none() {
oa.1 = Some(Arc::new(oa.0.to_string()));
}
Ok(oa.1.as_ref().unwrap().to_string())
}
}

Expand Down
18 changes: 10 additions & 8 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ pub struct AppState {
pub delete: Mutex<Router<usize>>,
pub options: Mutex<Router<usize>>,
pub websocket: Mutex<Router<usize>>,
pub openapi: Mutex<serde_json::Value>,
pub openapi: Mutex<(serde_json::Value, Option<Arc<String>>)>,
/// When `Some`, route matching uses these tables without taking per-router mutexes
/// (populated in [`App::freeze`](crate::App::freeze)).
pub compiled: Option<Arc<CompiledRouters>>,
Expand Down Expand Up @@ -132,7 +132,7 @@ impl AppState {
delete: Mutex::new(Router::new()),
options: Mutex::new(Router::new()),
websocket: Mutex::new(Router::new()),
openapi: Mutex::new(openapi),
openapi: Mutex::new((openapi, None)),
compiled: None,
frozen: false,
include_openapi: true,
Expand Down Expand Up @@ -213,6 +213,7 @@ pub fn match_ws_route_compiled(
/// Lookup an HTTP route in a precomputed [`CompiledRouters`] (lock-free).
///
/// Returns ``None`` for unsupported method, ``Some(None)`` for no match, ``Some(Some(...))`` on hit.
#[allow(clippy::type_complexity)]
pub fn match_route_compiled(
compiled: &CompiledRouters,
method: &str,
Expand Down Expand Up @@ -353,26 +354,27 @@ fn methods_matching_path(state: &AppState, path: &str) -> Vec<String> {
/// Returns route index and path params, or `None` if the method is unsupported; `Some(None)` if
/// no match; `Some(Some)` on success. Uses [`CompiledRouters`] when set (lock-free).
#[cfg(test)]
#[allow(clippy::type_complexity)]
fn match_route(
state: &AppState,
method: &str,
path: &str,
) -> Option<Option<(usize, HashMap<String, String>)>> {
) -> Option<Option<(usize, Vec<(String, String)>)>> {
if let Some(c) = &state.compiled {
let g = router_for_compiled(c, method)?;
return Some(g.at(path).ok().map(|m| {
let mut pmap = HashMap::new();
let mut pmap = Vec::new();
for (k, v) in m.params.iter() {
pmap.insert(k.to_string(), v.to_string());
pmap.push((k.to_string(), v.to_string()));
}
(*m.value, pmap)
}));
}
let g = map_method_router(state, method)?;
Some(g.at(path).ok().map(|m| {
let mut pmap = HashMap::new();
let mut pmap = Vec::new();
for (k, v) in m.params.iter() {
pmap.insert(k.to_string(), v.to_string());
pmap.push((k.to_string(), v.to_string()));
}
(*m.value, pmap)
}))
Expand All @@ -394,7 +396,7 @@ mod tests {
assert_eq!(pre, post);
let inner = pre.expect("match");
assert_eq!(inner.0, 7);
assert_eq!(inner.1.get("id").map(String::as_str), Some("5"));
assert_eq!(inner.1.iter().find(|(k, _)| k == "id").map(|(_, v)| v.as_str()), Some("5"));
}

#[test]
Expand Down
Loading