grove

@7 Keep serving after a request panics

by claude for antonky · landed by antonky · · from #6 · files at @7

Files

src/api.rsmodified+14 −8
6666 Ok(Api { store: Mutex::new(store), objects })
6767 }
6868
69 /// The store, even if an earlier request panicked while holding it. Store writes are
70 /// all-or-nothing, so a panic can't leave it half-changed.
71 fn store(&self) -> std::sync::MutexGuard<'_, Store> {
72 self.store.lock().unwrap_or_else(|poisoned| poisoned.into_inner())
73 }
74
6975 pub fn handle(&self, req: Request) -> Response {
7076 match self.route(&req) {
7177 Ok(r) | Err(r) => r,
7581 fn actor(&self, req: &Request) -> Result<Option<Actor>, Response> {
7682 let Some(h) = req.header("authorization") else { return Ok(None) };
7783 let token = h.strip_prefix("Bearer ").ok_or_else(|| err(401, "use Authorization: Bearer <token>"))?;
78 let store = self.store.lock().unwrap();
84 let store = self.store();
7985 store.authenticate(token.trim()).map(Some).ok_or_else(|| err(401, "unknown or revoked token"))
8086 }
8187
9096 ("GET", ["api", "health"]) => ok(obj([("ok", true.into())])),
9197 ("GET", ["api", "whoami"]) => {
9298 let a = self.required(req)?;
93 let store = self.store.lock().unwrap();
99 let store = self.store();
94100 let agent_name = a.agent.as_ref().and_then(|id| store.state().agent(id)).map(|g| g.name.clone());
95101 ok(obj([("person", a.person.clone().into()), ("agent", a.agent.clone().into()), ("agent_name", agent_name.into())]))
96102 }
129135 return Err(err(403, "agents are registered in person"));
130136 }
131137 let name = field(&body(req)?, "name")?.to_string();
132 let id = self.store.lock().unwrap().add_agent(&a.person, &name).map_err(from_store)?;
138 let id = self.store().add_agent(&a.person, &name).map_err(from_store)?;
133139 ok(obj([("id", id.into())]))
134140 }
135141 ("POST", ["api", "tokens"]) => {
139145 }
140146 let b = body(req)?;
141147 let agent = b.get("agent").and_then(Value::as_str).map(str::to_string);
142 let token = self.store.lock().unwrap().issue_token(&a.person, agent.as_deref()).map_err(from_store)?;
148 let token = self.store().issue_token(&a.person, agent.as_deref()).map_err(from_store)?;
143149 ok(obj([("token", token.into())]))
144150 }
145151 ("POST", ["api", "groups"]) => {
146152 let a = self.required(req)?;
147153 let name = field(&body(req)?, "name")?.to_string();
148 self.store.lock().unwrap().create_group(&a, &name).map_err(from_store)?;
154 self.store().create_group(&a, &name).map_err(from_store)?;
149155 ok(obj([("name", name.into())]))
150156 }
151157 ("POST", ["api", "groups", group, "members"]) => {
152158 let a = self.required(req)?;
153159 let person = field(&body(req)?, "person")?.to_string();
154 self.store.lock().unwrap().add_member(&a, group, &person).map_err(from_store)?;
160 self.store().add_member(&a, group, &person).map_err(from_store)?;
155161 ok(obj([("added", person.into())]))
156162 }
157163
162168 let owner = b.get("owner").and_then(Value::as_str).unwrap_or(&a.person).to_string();
163169 let name = field(&b, "name")?.to_string();
164170 let public = !matches!(b.get("public"), Some(Value::Bool(false)));
165 self.store.lock().unwrap().create_project(&a, &owner, &name, public).map_err(from_store)?;
171 self.store().create_project(&a, &owner, &name, public).map_err(from_store)?;
166172 ok(obj([("owner", owner.into()), ("name", name.into())]))
167173 }
168174 (_, ["api", "projects", owner, name, rest @ ..]) => self.project_route(req, m, owner, name, rest),
172178
173179 fn project_route(&self, req: &Request, m: &str, owner: &str, name: &str, rest: &[&str]) -> Out {
174180 let actor = self.actor(req)?;
175 let mut store = self.store.lock().unwrap();
181 let mut store = self.store();
176182 {
177183 let p = store.project(owner, name).map_err(|_| err(404, format!("no project {owner}/{name}")))?;
178184 let can_read = p.public || actor.as_ref().is_some_and(|a| store.state().owns(&p.owner, &a.person));
src/http.rsmodified+3 −1
153153 let _ = stream.set_read_timeout(Some(Duration::from_secs(60)));
154154 let _ = stream.set_write_timeout(Some(Duration::from_secs(60)));
155155 let response = match read_request(&stream) {
156 Ok(req) => handler(req),
156 // A bug in one request must not take the server down: answer 500 and carry on.
157 Ok(req) => std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| handler(req)))
158 .unwrap_or_else(|_| Response::error(500, "internal error")),
157159 Err((status, msg)) => Response::error(status, &msg),
158160 };
159161 let _ = write_response(&mut stream, &response);