Server: simplifies error related code

pull/300/head
Tpt 2 years ago committed by Thomas Tanon
parent 7f89baad87
commit 3f7ff6843d
  1. 437
      server/src/main.rs

@ -6,7 +6,7 @@ use oxhttp::Server;
use oxigraph::io::{DatasetFormat, DatasetSerializer, GraphFormat, GraphSerializer};
use oxigraph::model::{GraphName, GraphNameRef, IriParseError, NamedNode, NamedOrBlankNode};
use oxigraph::sparql::{Query, QueryResults, Update};
use oxigraph::store::{BulkLoader, Store};
use oxigraph::store::{BulkLoader, LoaderError, Store};
use oxiri::Iri;
use rand::random;
use rayon_core::ThreadPoolBuilder;
@ -137,7 +137,10 @@ pub fn main() -> anyhow::Result<()> {
Ok(())
}
Command::Serve { bind } => {
let mut server = Server::new(move |request| handle_request(request, store.clone()));
let mut server = Server::new(move |request| {
handle_request(request, store.clone())
.unwrap_or_else(|(status, message)| error(status, message))
});
server.set_global_timeout(HTTP_TIMEOUT);
server
.set_server_name(concat!("Oxigraph/", env!("CARGO_PKG_VERSION")))
@ -154,7 +157,7 @@ fn bulk_load(
reader: impl Read,
format: GraphOrDatasetFormat,
base_iri: Option<&str>,
) -> anyhow::Result<()> {
) -> Result<(), LoaderError> {
let reader = BufReader::new(reader);
match format {
GraphOrDatasetFormat::Graph(format) => {
@ -217,24 +220,26 @@ impl GraphOrDatasetFormat {
}
}
fn handle_request(request: &mut Request, store: Store) -> Response {
type HttpError = (Status, String);
fn handle_request(request: &mut Request, store: Store) -> Result<Response, HttpError> {
match (request.url().path(), request.method().as_ref()) {
("/", "HEAD") => Response::builder(Status::OK)
("/", "HEAD") => Ok(Response::builder(Status::OK)
.with_header(HeaderName::CONTENT_TYPE, "text_html")
.unwrap()
.build(),
("/", "GET") => Response::builder(Status::OK)
.build()),
("/", "GET") => Ok(Response::builder(Status::OK)
.with_header(HeaderName::CONTENT_TYPE, "text_html")
.unwrap()
.with_body(HTML_ROOT_PAGE),
("/logo.svg", "HEAD") => Response::builder(Status::OK)
.with_body(HTML_ROOT_PAGE)),
("/logo.svg", "HEAD") => Ok(Response::builder(Status::OK)
.with_header(HeaderName::CONTENT_TYPE, "image/svg+xml")
.unwrap()
.build(),
("/logo.svg", "GET") => Response::builder(Status::OK)
.build()),
("/logo.svg", "GET") => Ok(Response::builder(Status::OK)
.with_header(HeaderName::CONTENT_TYPE, "image/svg+xml")
.unwrap()
.with_body(LOGO),
.with_body(LOGO)),
("/query", "GET") => {
configure_and_evaluate_sparql_query(store, &[url_query(request)], None, request)
}
@ -242,13 +247,11 @@ fn handle_request(request: &mut Request, store: Store) -> Response {
if let Some(content_type) = content_type(request) {
if content_type == "application/sparql-query" {
let mut buffer = String::new();
if let Err(e) = request
request
.body_mut()
.take(MAX_SPARQL_BODY_SIZE)
.read_to_string(&mut buffer)
{
return bad_request(e);
}
.map_err(bad_request)?;
configure_and_evaluate_sparql_query(
store,
&[url_query(request)],
@ -257,13 +260,11 @@ fn handle_request(request: &mut Request, store: Store) -> Response {
)
} else if content_type == "application/x-www-form-urlencoded" {
let mut buffer = Vec::new();
if let Err(e) = request
request
.body_mut()
.take(MAX_SPARQL_BODY_SIZE)
.read_to_end(&mut buffer)
{
return bad_request(e);
}
.map_err(bad_request)?;
configure_and_evaluate_sparql_query(
store,
&[url_query(request), &buffer],
@ -271,23 +272,21 @@ fn handle_request(request: &mut Request, store: Store) -> Response {
request,
)
} else {
unsupported_media_type(&content_type)
Err(unsupported_media_type(&content_type))
}
} else {
bad_request("No Content-Type given")
Err(bad_request("No Content-Type given"))
}
}
("/update", "POST") => {
if let Some(content_type) = content_type(request) {
if content_type == "application/sparql-update" {
let mut buffer = String::new();
if let Err(e) = request
request
.body_mut()
.take(MAX_SPARQL_BODY_SIZE)
.read_to_string(&mut buffer)
{
return bad_request(e);
}
.map_err(bad_request)?;
configure_and_evaluate_sparql_update(
store,
&[url_query(request)],
@ -296,13 +295,11 @@ fn handle_request(request: &mut Request, store: Store) -> Response {
)
} else if content_type == "application/x-www-form-urlencoded" {
let mut buffer = Vec::new();
if let Err(e) = request
request
.body_mut()
.take(MAX_SPARQL_BODY_SIZE)
.read_to_end(&mut buffer)
{
return bad_request(e);
}
.map_err(bad_request)?;
configure_and_evaluate_sparql_update(
store,
&[url_query(request), &buffer],
@ -310,33 +307,26 @@ fn handle_request(request: &mut Request, store: Store) -> Response {
request,
)
} else {
unsupported_media_type(&content_type)
return Err(unsupported_media_type(&content_type));
}
} else {
bad_request("No Content-Type given")
Err(bad_request("No Content-Type given"))
}
}
(path, "GET") if path.starts_with("/store") => {
if let Some(target) = match store_target(request) {
Ok(target) => target,
Err(error) => return error,
} {
if let Some(target) = store_target(request)? {
if !match &target {
NamedGraphName::DefaultGraph => true,
NamedGraphName::NamedNode(target) => match store.contains_named_graph(target) {
Ok(r) => r,
Err(e) => return internal_server_error(e),
},
NamedGraphName::NamedNode(target) => store
.contains_named_graph(target)
.map_err(internal_server_error)?,
} {
return error(
return Err((
Status::NOT_FOUND,
format!("The graph {} does not exists", GraphName::from(target)),
);
));
}
let format = match graph_content_negotiation(request) {
Ok(format) => format,
Err(response) => return response,
};
let format = graph_content_negotiation(request)?;
let triples = store.quads_for_pattern(
None,
None,
@ -362,10 +352,7 @@ fn handle_request(request: &mut Request, store: Store) -> Response {
format.media_type(),
)
} else {
let format = match dataset_content_negotiation(request) {
Ok(format) => format,
Err(response) => return response,
};
let format = dataset_content_negotiation(request)?;
ReadForWrite::build_response(
move |w| {
Ok((
@ -388,198 +375,164 @@ fn handle_request(request: &mut Request, store: Store) -> Response {
}
(path, "PUT") if path.starts_with("/store") => {
if let Some(content_type) = content_type(request) {
if let Some(target) = match store_target(request) {
Ok(target) => target,
Err(error) => return error,
} {
if let Some(target) = store_target(request)? {
if let Some(format) = GraphFormat::from_media_type(&content_type) {
let new = !match &target {
NamedGraphName::NamedNode(target) => {
if match store.contains_named_graph(target) {
Ok(r) => r,
Err(e) => return internal_server_error(e),
} {
if let Err(e) = store.clear_graph(target) {
return internal_server_error(e);
}
if store
.contains_named_graph(target)
.map_err(internal_server_error)?
{
store.clear_graph(target).map_err(internal_server_error)?;
true
} else {
if let Err(e) = store.insert_named_graph(target) {
return internal_server_error(e);
}
store
.insert_named_graph(target)
.map_err(internal_server_error)?;
false
}
}
NamedGraphName::DefaultGraph => {
if let Err(e) = store.clear_graph(GraphNameRef::DefaultGraph) {
return internal_server_error(e);
}
store
.clear_graph(GraphNameRef::DefaultGraph)
.map_err(internal_server_error)?;
true
}
};
if let Err(e) = store.load_graph(
store
.load_graph(
BufReader::new(request.body_mut()),
format,
GraphName::from(target).as_ref(),
None,
) {
return bad_request(e);
}
Response::builder(if new {
)
.map_err(bad_request)?;
Ok(Response::builder(if new {
Status::CREATED
} else {
Status::NO_CONTENT
})
.build()
.build())
} else {
unsupported_media_type(&content_type)
Err(unsupported_media_type(&content_type))
}
} else if let Some(format) = DatasetFormat::from_media_type(&content_type) {
if let Err(e) = store.clear() {
return internal_server_error(e);
}
if let Err(e) =
store.load_dataset(BufReader::new(request.body_mut()), format, None)
{
return internal_server_error(e);
}
Response::builder(Status::NO_CONTENT).build()
store.clear().map_err(internal_server_error)?;
store
.load_dataset(BufReader::new(request.body_mut()), format, None)
.map_err(internal_server_error)?;
Ok(Response::builder(Status::NO_CONTENT).build())
} else {
unsupported_media_type(&content_type)
Err(unsupported_media_type(&content_type))
}
} else {
bad_request("No Content-Type given")
Err(bad_request("No Content-Type given"))
}
}
(path, "DELETE") if path.starts_with("/store") => {
if let Some(target) = match store_target(request) {
Ok(target) => target,
Err(error) => return error,
} {
if let Some(target) = store_target(request)? {
match target {
NamedGraphName::DefaultGraph => {
if let Err(e) = store.clear_graph(GraphNameRef::DefaultGraph) {
return internal_server_error(e);
}
}
NamedGraphName::DefaultGraph => store
.clear_graph(GraphNameRef::DefaultGraph)
.map_err(internal_server_error)?,
NamedGraphName::NamedNode(target) => {
if match store.contains_named_graph(&target) {
Ok(r) => r,
Err(e) => return internal_server_error(e),
} {
if let Err(e) = store.remove_named_graph(&target) {
return internal_server_error(e);
}
if store
.contains_named_graph(&target)
.map_err(internal_server_error)?
{
store
.remove_named_graph(&target)
.map_err(internal_server_error)?;
} else {
return error(
return Err((
Status::NOT_FOUND,
format!("The graph {} does not exists", target),
);
));
}
}
}
} else if let Err(e) = store.clear() {
return internal_server_error(e);
} else {
store.clear().map_err(internal_server_error)?;
}
Response::builder(Status::NO_CONTENT).build()
Ok(Response::builder(Status::NO_CONTENT).build())
}
(path, "POST") if path.starts_with("/store") => {
if let Some(content_type) = content_type(request) {
if let Some(target) = match store_target(request) {
Ok(target) => target,
Err(error) => return error,
} {
if let Some(target) = store_target(request)? {
if let Some(format) = GraphFormat::from_media_type(&content_type) {
let new = !match &target {
NamedGraphName::NamedNode(target) => {
match store.contains_named_graph(target) {
Ok(r) => r,
Err(e) => return internal_server_error(e),
}
}
NamedGraphName::NamedNode(target) => store
.contains_named_graph(target)
.map_err(internal_server_error)?,
NamedGraphName::DefaultGraph => true,
};
if let Err(e) = store.load_graph(
store
.load_graph(
BufReader::new(request.body_mut()),
format,
GraphName::from(target).as_ref(),
None,
) {
return bad_request(e);
}
Response::builder(if new {
)
.map_err(bad_request)?;
Ok(Response::builder(if new {
Status::CREATED
} else {
Status::NO_CONTENT
})
.build()
.build())
} else {
unsupported_media_type(&content_type)
Err(unsupported_media_type(&content_type))
}
} else if let Some(format) = DatasetFormat::from_media_type(&content_type) {
if let Err(e) =
store.load_dataset(BufReader::new(request.body_mut()), format, None)
{
return bad_request(e);
}
Response::builder(Status::NO_CONTENT).build()
store
.load_dataset(BufReader::new(request.body_mut()), format, None)
.map_err(bad_request)?;
Ok(Response::builder(Status::NO_CONTENT).build())
} else if let Some(format) = GraphFormat::from_media_type(&content_type) {
let graph =
match resolve_with_base(request, &format!("/store/{:x}", random::<u128>()))
{
Ok(graph) => graph,
Err(e) => return e,
};
if let Err(e) =
store.load_graph(BufReader::new(request.body_mut()), format, &graph, None)
{
return bad_request(e);
}
Response::builder(Status::CREATED)
resolve_with_base(request, &format!("/store/{:x}", random::<u128>()))?;
store
.load_graph(BufReader::new(request.body_mut()), format, &graph, None)
.map_err(bad_request)?;
Ok(Response::builder(Status::CREATED)
.with_header(HeaderName::LOCATION, graph.into_string())
.unwrap()
.build()
.build())
} else {
unsupported_media_type(&content_type)
Err(unsupported_media_type(&content_type))
}
} else {
bad_request("No Content-Type given")
Err(bad_request("No Content-Type given"))
}
}
(path, "HEAD") if path.starts_with("/store") => {
if let Some(target) = match store_target(request) {
Ok(target) => target,
Err(error) => return error,
} {
if let Some(target) = store_target(request)? {
if !match &target {
NamedGraphName::DefaultGraph => true,
NamedGraphName::NamedNode(target) => match store.contains_named_graph(target) {
Ok(r) => r,
Err(e) => return internal_server_error(e),
},
NamedGraphName::NamedNode(target) => store
.contains_named_graph(target)
.map_err(internal_server_error)?,
} {
return error(
return Err((
Status::NOT_FOUND,
format!("The graph {} does not exists", GraphName::from(target)),
);
));
}
Response::builder(Status::OK).build()
} else {
Response::builder(Status::OK).build()
}
Ok(Response::builder(Status::OK).build())
}
_ => error(
_ => Err((
Status::NOT_FOUND,
format!(
"{} {} is not supported by this server",
request.method(),
request.url().path()
),
),
)),
}
}
fn base_url(request: &Request) -> Result<String, Response> {
fn base_url(request: &Request) -> Result<String, HttpError> {
let mut url = request.url().clone();
if let Some(host) = request.url().host_str() {
url.set_host(Some(host)).map_err(bad_request)?;
@ -589,7 +542,7 @@ fn base_url(request: &Request) -> Result<String, Response> {
Ok(url.into())
}
fn resolve_with_base(request: &Request, url: &str) -> Result<NamedNode, Response> {
fn resolve_with_base(request: &Request, url: &str) -> Result<NamedNode, HttpError> {
Ok(NamedNode::new_unchecked(
Iri::parse(base_url(request)?)
.map_err(bad_request)?
@ -608,7 +561,7 @@ fn configure_and_evaluate_sparql_query(
encoded: &[&[u8]],
mut query: Option<String>,
request: &Request,
) -> Response {
) -> Result<Response, HttpError> {
let mut default_graph_uris = Vec::new();
let mut named_graph_uris = Vec::new();
let mut use_default_graph_as_union = false;
@ -617,7 +570,7 @@ fn configure_and_evaluate_sparql_query(
match k.as_ref() {
"query" => {
if query.is_some() {
return bad_request("Multiple query parameters provided");
return Err(bad_request("Multiple query parameters provided"));
}
query = Some(v.into_owned())
}
@ -638,7 +591,7 @@ fn configure_and_evaluate_sparql_query(
request,
)
} else {
bad_request("You should set the 'query' parameter")
Err(bad_request("You should set the 'query' parameter"))
}
}
@ -649,58 +602,37 @@ fn evaluate_sparql_query(
default_graph_uris: Vec<String>,
named_graph_uris: Vec<String>,
request: &Request,
) -> Response {
let mut query = match Query::parse(
&query,
Some(&match base_url(request) {
Ok(url) => url,
Err(r) => return r,
}),
) {
Ok(query) => query,
Err(e) => return bad_request(e),
};
) -> Result<Response, HttpError> {
let mut query = Query::parse(&query, Some(&base_url(request)?)).map_err(bad_request)?;
if use_default_graph_as_union {
if !default_graph_uris.is_empty() || !named_graph_uris.is_empty() {
return bad_request(
return Err(bad_request(
"default-graph-uri or named-graph-uri and union-default-graph should not be set at the same time"
);
));
}
query.dataset_mut().set_default_graph_as_union()
} else if !default_graph_uris.is_empty() || !named_graph_uris.is_empty() {
query.dataset_mut().set_default_graph(
match default_graph_uris
default_graph_uris
.into_iter()
.map(|e| Ok(NamedNode::new(e)?.into()))
.collect::<Result<Vec<GraphName>, IriParseError>>()
{
Ok(default_graph_uris) => default_graph_uris,
Err(e) => return bad_request(e),
},
.map_err(bad_request)?,
);
query.dataset_mut().set_available_named_graphs(
match named_graph_uris
named_graph_uris
.into_iter()
.map(|e| Ok(NamedNode::new(e)?.into()))
.collect::<Result<Vec<NamedOrBlankNode>, IriParseError>>()
{
Ok(named_graph_uris) => named_graph_uris,
Err(e) => return bad_request(e),
},
.map_err(bad_request)?,
);
}
let results = match store.query(query) {
Ok(results) => results,
Err(e) => return internal_server_error(e),
};
let results = store.query(query).map_err(internal_server_error)?;
match results {
QueryResults::Solutions(solutions) => {
let format = match query_results_content_negotiation(request) {
Ok(format) => format,
Err(response) => return response,
};
let format = query_results_content_negotiation(request)?;
ReadForWrite::build_response(
move |w| {
Ok((
@ -722,26 +654,18 @@ fn evaluate_sparql_query(
)
}
QueryResults::Boolean(result) => {
let format = match query_results_content_negotiation(request) {
Ok(format) => format,
Err(response) => return response,
};
let format = query_results_content_negotiation(request)?;
let mut body = Vec::new();
if let Err(e) =
QueryResultsSerializer::from_format(format).write_boolean_result(&mut body, result)
{
return internal_server_error(e);
}
Response::builder(Status::OK)
QueryResultsSerializer::from_format(format)
.write_boolean_result(&mut body, result)
.map_err(internal_server_error)?;
Ok(Response::builder(Status::OK)
.with_header(HeaderName::CONTENT_TYPE, format.media_type())
.unwrap()
.with_body(body)
.with_body(body))
}
QueryResults::Graph(triples) => {
let format = match graph_content_negotiation(request) {
Ok(format) => format,
Err(response) => return response,
};
let format = graph_content_negotiation(request)?;
ReadForWrite::build_response(
move |w| {
Ok((
@ -769,7 +693,7 @@ fn configure_and_evaluate_sparql_update(
encoded: &[&[u8]],
mut update: Option<String>,
request: &Request,
) -> Response {
) -> Result<Response, HttpError> {
let mut use_default_graph_as_union = false;
let mut default_graph_uris = Vec::new();
let mut named_graph_uris = Vec::new();
@ -778,7 +702,7 @@ fn configure_and_evaluate_sparql_update(
match k.as_ref() {
"update" => {
if update.is_some() {
return bad_request("Multiple update parameters provided");
return Err(bad_request("Multiple update parameters provided"));
}
update = Some(v.into_owned())
}
@ -799,7 +723,7 @@ fn configure_and_evaluate_sparql_update(
request,
)
} else {
bad_request("You should set the 'update' parameter")
Err(bad_request("You should set the 'update' parameter"))
}
}
@ -810,69 +734,50 @@ fn evaluate_sparql_update(
default_graph_uris: Vec<String>,
named_graph_uris: Vec<String>,
request: &Request,
) -> Response {
let mut update = match Update::parse(
&update,
Some(
match base_url(request) {
Ok(url) => url,
Err(e) => return e,
}
.as_str(),
),
) {
Ok(update) => update,
Err(e) => return bad_request(e),
};
) -> Result<Response, HttpError> {
let mut update =
Update::parse(&update, Some(base_url(request)?.as_str())).map_err(bad_request)?;
if use_default_graph_as_union {
if !default_graph_uris.is_empty() || !named_graph_uris.is_empty() {
return bad_request(
return Err(bad_request(
"using-graph-uri or using-named-graph-uri and using-union-graph should not be set at the same time"
);
));
}
for using in update.using_datasets_mut() {
if !using.is_default_dataset() {
return bad_request(
return Err(bad_request(
"using-union-graph must not be used with a SPARQL UPDATE containing USING",
);
));
}
using.set_default_graph_as_union();
}
} else if !default_graph_uris.is_empty() || !named_graph_uris.is_empty() {
let default_graph_uris = match default_graph_uris
let default_graph_uris = default_graph_uris
.into_iter()
.map(|e| Ok(NamedNode::new(e)?.into()))
.collect::<Result<Vec<GraphName>, IriParseError>>()
{
Ok(default_graph_uris) => default_graph_uris,
Err(e) => return bad_request(e),
};
let named_graph_uris = match named_graph_uris
.map_err(bad_request)?;
let named_graph_uris = named_graph_uris
.into_iter()
.map(|e| Ok(NamedNode::new(e)?.into()))
.collect::<Result<Vec<NamedOrBlankNode>, IriParseError>>()
{
Ok(named_graph_uris) => named_graph_uris,
Err(e) => return bad_request(e),
};
.map_err(bad_request)?;
for using in update.using_datasets_mut() {
if !using.is_default_dataset() {
return bad_request(
return Err(bad_request(
"using-graph-uri and using-named-graph-uri must not be used with a SPARQL UPDATE containing USING",
);
));
}
using.set_default_graph(default_graph_uris.clone());
using.set_available_named_graphs(named_graph_uris.clone());
}
}
if let Err(e) = store.update(update) {
return internal_server_error(e);
}
Response::builder(Status::NO_CONTENT).build()
store.update(update).map_err(internal_server_error)?;
Ok(Response::builder(Status::NO_CONTENT).build())
}
fn store_target(request: &Request) -> Result<Option<NamedGraphName>, Response> {
fn store_target(request: &Request) -> Result<Option<NamedGraphName>, HttpError> {
if request.url().path() == "/store" {
let mut graph = None;
let mut default = false;
@ -922,7 +827,7 @@ impl From<NamedGraphName> for GraphName {
}
}
fn graph_content_negotiation(request: &Request) -> Result<GraphFormat, Response> {
fn graph_content_negotiation(request: &Request) -> Result<GraphFormat, HttpError> {
content_negotiation(
request,
&[
@ -934,7 +839,7 @@ fn graph_content_negotiation(request: &Request) -> Result<GraphFormat, Response>
)
}
fn dataset_content_negotiation(request: &Request) -> Result<DatasetFormat, Response> {
fn dataset_content_negotiation(request: &Request) -> Result<DatasetFormat, HttpError> {
content_negotiation(
request,
&[
@ -945,7 +850,7 @@ fn dataset_content_negotiation(request: &Request) -> Result<DatasetFormat, Respo
)
}
fn query_results_content_negotiation(request: &Request) -> Result<QueryResultsFormat, Response> {
fn query_results_content_negotiation(request: &Request) -> Result<QueryResultsFormat, HttpError> {
content_negotiation(
request,
&[
@ -962,7 +867,7 @@ fn content_negotiation<F>(
request: &Request,
supported: &[&str],
parse: impl Fn(&str) -> Option<F>,
) -> Result<F, Response> {
) -> Result<F, HttpError> {
let default = HeaderValue::default();
let header = request
.header(&HeaderName::ACCEPT)
@ -1015,13 +920,13 @@ fn content_negotiation<F>(
}
let result = result.ok_or_else(|| {
error(
(
Status::NOT_ACCEPTABLE,
format!("The available Content-Types are {}", supported.join(", "),),
)
})?;
parse(result).ok_or_else(|| error(Status::INTERNAL_SERVER_ERROR, "Unknown media type"))
parse(result).ok_or_else(|| internal_server_error("Unknown media type"))
}
fn content_type(request: &Request) -> Option<String> {
@ -1042,20 +947,20 @@ fn error(status: Status, message: impl fmt::Display) -> Response {
.with_body(message.to_string())
}
fn bad_request(message: impl fmt::Display) -> Response {
error(Status::BAD_REQUEST, message)
fn bad_request(message: impl fmt::Display) -> HttpError {
(Status::BAD_REQUEST, message.to_string())
}
fn unsupported_media_type(content_type: &str) -> Response {
error(
fn unsupported_media_type(content_type: &str) -> HttpError {
(
Status::UNSUPPORTED_MEDIA_TYPE,
format!("No supported content Content-Type given: {}", content_type),
)
}
fn internal_server_error(message: impl fmt::Display) -> Response {
fn internal_server_error(message: impl fmt::Display) -> HttpError {
eprintln!("Internal server error: {}", message);
error(Status::INTERNAL_SERVER_ERROR, message)
(Status::INTERNAL_SERVER_ERROR, message.to_string())
}
/// Hacky tool to allow implementing read on top of a write loop
@ -1071,12 +976,13 @@ impl<O: 'static, U: (Fn(O) -> io::Result<Option<O>>) + 'static> ReadForWrite<O,
initial_state_builder: impl FnOnce(ReadForWriteWriter) -> io::Result<O>,
add_more_data: U,
content_type: &'static str,
) -> Response {
) -> Result<Response, HttpError> {
let buffer = Rc::new(RefCell::new(Vec::new()));
match initial_state_builder(ReadForWriteWriter {
let state = initial_state_builder(ReadForWriteWriter {
buffer: buffer.clone(),
}) {
Ok(state) => Response::builder(Status::OK)
})
.map_err(internal_server_error)?;
Ok(Response::builder(Status::OK)
.with_header(HeaderName::CONTENT_TYPE, content_type)
.unwrap()
.with_body(Body::from_read(Self {
@ -1084,9 +990,7 @@ impl<O: 'static, U: (Fn(O) -> io::Result<Option<O>>) + 'static> ReadForWrite<O,
position: 0,
add_more_data,
state: Some(state),
})),
Err(e) => internal_server_error(e),
}
})))
}
}
@ -1798,6 +1702,7 @@ mod tests {
fn exec(&self, mut request: Request) -> Response {
handle_request(&mut request, self.store.clone())
.unwrap_or_else(|(status, message)| error(status, message))
}
fn test_status(&self, request: Request, expected_status: Status) {

Loading…
Cancel
Save