Server: Add a nicer error when writes are not allowed

pull/417/head
Tpt 2 years ago committed by Thomas Tanon
parent 5eaa388312
commit 53edaf9d11
  1. 59
      server/src/main.rs

@ -173,10 +173,10 @@ pub fn main() -> anyhow::Result<()> {
Store::new()
}?,
bind,
true,
false,
),
Command::ServeReadOnly { location, bind } => {
serve(Store::open_read_only(location)?, bind, false)
serve(Store::open_read_only(location)?, bind, true)
}
Command::ServeSecondary {
primary_location,
@ -189,7 +189,7 @@ pub fn main() -> anyhow::Result<()> {
Store::open_secondary(primary_location)
}?,
bind,
false,
true,
),
Command::Backup {
location,
@ -486,9 +486,9 @@ impl FromStr for GraphOrDatasetFormat {
}
}
fn serve(store: Store, bind: String, allow_writes: bool) -> anyhow::Result<()> {
fn serve(store: Store, bind: String, read_only: bool) -> anyhow::Result<()> {
let mut server = Server::new(move |request| {
handle_request(request, store.clone(), allow_writes)
handle_request(request, store.clone(), read_only)
.unwrap_or_else(|(status, message)| error(status, message))
});
server.set_global_timeout(HTTP_TIMEOUT);
@ -503,7 +503,7 @@ type HttpError = (Status, String);
fn handle_request(
request: &mut Request,
store: Store,
allow_writes: bool,
read_only: bool,
) -> Result<Response, HttpError> {
match (request.url().path(), request.method().as_ref()) {
("/", "HEAD") => Ok(Response::builder(Status::OK)
@ -558,7 +558,10 @@ fn handle_request(
Err(unsupported_media_type(&content_type))
}
}
("/update", "POST") if allow_writes => {
("/update", "POST") => {
if read_only {
return Err(the_server_is_read_only());
}
let content_type =
content_type(request).ok_or_else(|| bad_request("No Content-Type given"))?;
if content_type == "application/sparql-update" {
@ -641,7 +644,10 @@ fn handle_request(
)
}
}
(path, "PUT") if path.starts_with("/store") && allow_writes => {
(path, "PUT") if path.starts_with("/store") => {
if read_only {
return Err(the_server_is_read_only());
}
let content_type =
content_type(request).ok_or_else(|| bad_request("No Content-Type given"))?;
if let Some(target) = store_target(request)? {
@ -684,7 +690,10 @@ fn handle_request(
Ok(Response::builder(Status::NO_CONTENT).build())
}
}
(path, "DELETE") if path.starts_with("/store") && allow_writes => {
(path, "DELETE") if path.starts_with("/store") => {
if read_only {
return Err(the_server_is_read_only());
}
if let Some(target) = store_target(request)? {
match target {
NamedGraphName::DefaultGraph => store
@ -711,7 +720,10 @@ fn handle_request(
}
Ok(Response::builder(Status::NO_CONTENT).build())
}
(path, "POST") if path.starts_with("/store") && allow_writes => {
(path, "POST") if path.starts_with("/store") => {
if read_only {
return Err(the_server_is_read_only());
}
let content_type =
content_type(request).ok_or_else(|| bad_request("No Content-Type given"))?;
if let Some(target) = store_target(request)? {
@ -1258,6 +1270,10 @@ fn bad_request(message: impl fmt::Display) -> HttpError {
(Status::BAD_REQUEST, message.to_string())
}
fn the_server_is_read_only() -> HttpError {
(Status::FORBIDDEN, "The server is read-only".into())
}
fn unsupported_media_type(content_type: &str) -> HttpError {
(
Status::UNSUPPORTED_MEDIA_TYPE,
@ -1813,6 +1829,19 @@ mod tests {
ServerTest::new()?.test_status(request, Status::BAD_REQUEST)
}
#[test]
fn post_update_read_only() -> Result<()> {
let request = Request::builder(Method::POST, "http://localhost/update".parse()?)
.with_header(HeaderName::CONTENT_TYPE, "application/sparql-update")?
.with_body(
"INSERT DATA { <http://example.com> <http://example.com> <http://example.com> }",
);
ServerTest::check_status(
ServerTest::new()?.exec_read_only(request),
Status::FORBIDDEN,
)
}
#[test]
fn graph_store_url_normalization() -> Result<()> {
let server = ServerTest::new()?;
@ -2148,12 +2177,20 @@ mod tests {
}
fn exec(&self, mut request: Request) -> Response {
handle_request(&mut request, self.store.clone(), false)
.unwrap_or_else(|(status, message)| error(status, message))
}
fn exec_read_only(&self, mut request: Request) -> Response {
handle_request(&mut request, self.store.clone(), true)
.unwrap_or_else(|(status, message)| error(status, message))
}
fn test_status(&self, request: Request, expected_status: Status) -> Result<()> {
let mut response = self.exec(request);
Self::check_status(self.exec(request), expected_status)
}
fn check_status(mut response: Response, expected_status: Status) -> Result<()> {
let mut buf = String::new();
response.body_mut().read_to_string(&mut buf)?;
assert_eq!(response.status(), expected_status, "Error message: {buf}");

Loading…
Cancel
Save