Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CORS Headers & Update HTTP Status Codes #893

Merged
merged 4 commits into from
Mar 18, 2022
Merged
Changes from 2 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
179 changes: 101 additions & 78 deletions sui/src/rest_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ use std::path::PathBuf;
use std::str::FromStr;
use std::sync::{Arc, RwLock};

use dropshot::{endpoint, Query, TypedBody};
use dropshot::{endpoint, Query, TypedBody, CONTENT_TYPE_JSON};
use dropshot::{
ApiDescription, ConfigDropshot, ConfigLogging, ConfigLoggingLevel, HttpError, HttpResponseOk,
HttpResponseUpdatedNoContent, HttpServerStarter, RequestContext,
};
use futures::lock::Mutex;
use futures::stream::{futures_unordered::FuturesUnordered, StreamExt as _};
use hyper::StatusCode;
use http::Response;
use hyper::{Body, StatusCode};
use move_core_types::identifier::Identifier;
use move_core_types::parser::parse_type_tag;
use move_core_types::value::MoveStructLayout;
Expand Down Expand Up @@ -221,9 +222,7 @@ network has been started on testnet or mainnet.
path = "/sui/genesis",
tags = [ "debug" ],
}]
async fn genesis(
rqctx: Arc<RequestContext<ServerContext>>,
) -> Result<HttpResponseOk<GenesisResponse>, HttpError> {
async fn genesis(rqctx: Arc<RequestContext<ServerContext>>) -> Result<Response<Body>, HttpError> {
let context = rqctx.context();
// Using a new working dir for genesis, this directory will be deleted when stop end point is called.
let working_dir = PathBuf::from(".").join(format!("{}", ObjectID::random()));
Expand Down Expand Up @@ -272,10 +271,14 @@ async fn genesis(

*context.server_state.lock().await = Some(state);

Ok(HttpResponseOk(GenesisResponse {
addresses: addresses_json,
network_config: network_config_json,
}))
custom_http_response(
StatusCode::OK,
GenesisResponse {
addresses: addresses_json,
network_config: network_config_json,
},
)
.map_err(|err| custom_http_error(StatusCode::BAD_REQUEST, format!("{err}")))
arun-koshy marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand All @@ -289,9 +292,7 @@ network has been started on testnet or main-net.
path = "/sui/start",
tags = [ "debug" ],
}]
async fn sui_start(
ctx: Arc<RequestContext<ServerContext>>,
) -> Result<HttpResponseOk<String>, HttpError> {
async fn sui_start(ctx: Arc<RequestContext<ServerContext>>) -> Result<Response<Body>, HttpError> {
let mut state = ctx.context().server_state.lock().await;
let state = state.as_mut().ok_or_else(server_state_error)?;

Expand Down Expand Up @@ -357,10 +358,11 @@ async fn sui_start(
)
})?;
}
Ok(HttpResponseOk(format!(
"Started {} authorities",
num_authorities
)))
custom_http_response(
StatusCode::OK,
format!("Started {} authorities", num_authorities),
)
.map_err(|err| custom_http_error(StatusCode::BAD_REQUEST, format!("{err}")))
}

/**
Expand Down Expand Up @@ -412,7 +414,7 @@ Retrieve all managed addresses for this client.
}]
async fn get_addresses(
ctx: Arc<RequestContext<ServerContext>>,
) -> Result<HttpResponseOk<GetAddressResponse>, HttpError> {
) -> Result<Response<Body>, HttpError> {
let mut state = ctx.context().server_state.lock().await;
let state = state.as_mut().ok_or_else(server_state_error)?;

Expand All @@ -422,17 +424,21 @@ async fn get_addresses(
for address in &addresses {
if let Err(err) = state.gateway.sync_account_state(*address).await {
return Err(custom_http_error(
StatusCode::FAILED_DEPENDENCY,
StatusCode::NOT_FOUND,
format!("Can't create client state: {err}"),
));
}
}
Ok(HttpResponseOk(GetAddressResponse {
addresses: addresses
.into_iter()
.map(|address| format!("{}", address))
.collect(),
}))
custom_http_response(
StatusCode::OK,
GetAddressResponse {
addresses: addresses
.into_iter()
.map(|address| format!("{}", address))
.collect(),
},
)
.map_err(|err| custom_http_error(StatusCode::FAILED_DEPENDENCY, format!("{err}")))
}

/**
Expand Down Expand Up @@ -481,14 +487,14 @@ Returns list of objects owned by an address.
async fn get_objects(
ctx: Arc<RequestContext<ServerContext>>,
query: Query<GetObjectsRequest>,
) -> Result<HttpResponseOk<GetObjectsResponse>, HttpError> {
) -> Result<Response<Body>, HttpError> {
let mut state = ctx.context().server_state.lock().await;
let state = state.as_mut().ok_or_else(server_state_error)?;
let get_objects_params = query.into_inner();
let address = get_objects_params.address;
let address = &decode_bytes_hex(address.as_str()).map_err(|error| {
custom_http_error(
StatusCode::FAILED_DEPENDENCY,
StatusCode::BAD_REQUEST,
format!("Could not decode address from hex {error}"),
)
})?;
Expand All @@ -515,7 +521,8 @@ async fn get_objects(
});
}

Ok(HttpResponseOk(GetObjectsResponse { objects }))
custom_http_response(StatusCode::OK, GetObjectsResponse { objects })
.map_err(|err| custom_http_error(StatusCode::FAILED_DEPENDENCY, format!("{err}")))
}

/**
Expand Down Expand Up @@ -553,7 +560,7 @@ Returns the schema for a specified object.
async fn object_schema(
ctx: Arc<RequestContext<ServerContext>>,
query: Query<GetObjectSchemaRequest>,
) -> Result<HttpResponseOk<ObjectSchemaResponse>, HttpError> {
) -> Result<Response<Body>, HttpError> {
let mut state = ctx.context().server_state.lock().await;
let state = state.as_mut().ok_or_else(server_state_error)?;
let object_info_params = query.into_inner();
Expand All @@ -562,7 +569,7 @@ async fn object_schema(
Ok(object_id) => object_id,
Err(error) => {
return Err(custom_http_error(
StatusCode::FAILED_DEPENDENCY,
StatusCode::BAD_REQUEST,
format!("{error}"),
));
}
Expand All @@ -572,31 +579,32 @@ async fn object_schema(
Ok(ObjectRead::Exists(_, _, layout)) => layout,
Ok(ObjectRead::Deleted(_)) => {
return Err(custom_http_error(
StatusCode::FAILED_DEPENDENCY,
StatusCode::NOT_FOUND,
format!("Object ({object_id}) was deleted."),
));
}
Ok(ObjectRead::NotExists(_)) => {
return Err(custom_http_error(
StatusCode::FAILED_DEPENDENCY,
StatusCode::NOT_FOUND,
format!("Object ({object_id}) does not exist."),
));
}
Err(error) => {
return Err(custom_http_error(
StatusCode::FAILED_DEPENDENCY,
StatusCode::NOT_FOUND,
format!("Error while getting object info: {:?}", error),
));
}
};

match serde_json::to_value(layout) {
Ok(schema) => Ok(HttpResponseOk(ObjectSchemaResponse { schema })),
Err(e) => Err(custom_http_error(
let schema = serde_json::to_value(layout).map_err(|error| {
custom_http_error(
StatusCode::FAILED_DEPENDENCY,
format!("Error while getting object info: {:?}", e),
)),
}
format!("Error while getting object info: {:?}", error),
)
})?;

custom_http_response(StatusCode::OK, ObjectSchemaResponse { schema })
.map_err(|err| custom_http_error(StatusCode::FAILED_DEPENDENCY, format!("{err}")))
}

/**
Expand Down Expand Up @@ -644,34 +652,31 @@ Returns the object information for a specified object.
async fn object_info(
ctx: Arc<RequestContext<ServerContext>>,
query: Query<GetObjectInfoRequest>,
) -> Result<HttpResponseOk<ObjectInfoResponse>, HttpError> {
) -> Result<Response<Body>, HttpError> {
let mut state = ctx.context().server_state.lock().await;
let state = state.as_mut().ok_or_else(server_state_error)?;

let object_info_params = query.into_inner();
let object_id = match ObjectID::try_from(object_info_params.object_id) {
Ok(object_id) => object_id,
Err(error) => {
return Err(custom_http_error(
StatusCode::FAILED_DEPENDENCY,
format!("{error}"),
));
}
};
let object_id = ObjectID::try_from(object_info_params.object_id)
.map_err(|error| custom_http_error(StatusCode::BAD_REQUEST, format!("{error}")))?;

let (_, object, layout) = get_object_info(state, object_id).await?;
let object_data = object.to_json(&layout).unwrap_or_else(|_| json!(""));
Ok(HttpResponseOk(ObjectInfoResponse {
owner: format!("{:?}", object.owner),
version: format!("{:?}", object.version().value()),
id: format!("{:?}", object.id()),
readonly: format!("{:?}", object.is_read_only()),
obj_type: object
.data
.type_()
.map_or("Unknown Type".to_owned(), |type_| format!("{}", type_)),
data: object_data,
}))
custom_http_response(
StatusCode::OK,
&ObjectInfoResponse {
owner: format!("{:?}", object.owner),
version: format!("{:?}", object.version().value()),
id: format!("{:?}", object.id()),
readonly: format!("{:?}", object.is_read_only()),
obj_type: object
.data
.type_()
.map_or("Unknown Type".to_owned(), |type_| format!("{}", type_)),
data: object_data,
},
)
.map_err(|err| custom_http_error(StatusCode::NOT_FOUND, format!("{err}")))
}

/**
Expand Down Expand Up @@ -729,25 +734,25 @@ Example TransferTransactionRequest
async fn transfer_object(
ctx: Arc<RequestContext<ServerContext>>,
request: TypedBody<TransferTransactionRequest>,
) -> Result<HttpResponseOk<TransactionResponse>, HttpError> {
) -> Result<Response<Body>, HttpError> {
let mut state = ctx.context().server_state.lock().await;
let state = state.as_mut().ok_or_else(server_state_error)?;
let transfer_order_params = request.into_inner();

let to_address =
decode_bytes_hex(transfer_order_params.to_address.as_str()).map_err(|error| {
custom_http_error(
StatusCode::FAILED_DEPENDENCY,
StatusCode::BAD_REQUEST,
format!("Could not decode to address from hex {error}"),
)
})?;
let object_id = ObjectID::try_from(transfer_order_params.object_id)
.map_err(|error| custom_http_error(StatusCode::FAILED_DEPENDENCY, format!("{error}")))?;
.map_err(|error| custom_http_error(StatusCode::BAD_REQUEST, format!("{error}")))?;
let gas_object_id = ObjectID::try_from(transfer_order_params.gas_object_id)
.map_err(|error| custom_http_error(StatusCode::FAILED_DEPENDENCY, format!("{error}")))?;
.map_err(|error| custom_http_error(StatusCode::BAD_REQUEST, format!("{error}")))?;
let owner = decode_bytes_hex(transfer_order_params.from_address.as_str()).map_err(|error| {
custom_http_error(
StatusCode::FAILED_DEPENDENCY,
StatusCode::BAD_REQUEST,
format!("Could not decode address from hex {error}"),
)
})?;
Expand All @@ -768,7 +773,7 @@ async fn transfer_object(
ExecutionStatus::Success { gas_used, .. } => gas_used,
ExecutionStatus::Failure { gas_used, error } => {
return Err(custom_http_error(
StatusCode::FAILED_DEPENDENCY,
StatusCode::CONFLICT,
format!(
"Error transferring object: {:#?}, gas used {}",
error, gas_used
Expand All @@ -780,19 +785,23 @@ async fn transfer_object(
}
Err(err) => {
return Err(custom_http_error(
StatusCode::FAILED_DEPENDENCY,
StatusCode::CONFLICT,
format!("Transfer error: {err}"),
));
}
};

let object_effects_summary = get_object_effects(state, effects).await?;

Ok(HttpResponseOk(TransactionResponse {
gas_used,
object_effects_summary: json!(object_effects_summary),
certificate: json!(cert),
}))
custom_http_response(
StatusCode::OK,
TransactionResponse {
gas_used,
object_effects_summary: json!(object_effects_summary),
certificate: json!(cert),
},
)
.map_err(|err| custom_http_error(StatusCode::FAILED_DEPENDENCY, format!("{err}")))
}

/**
Expand Down Expand Up @@ -890,15 +899,16 @@ Example CallRequest
async fn call(
ctx: Arc<RequestContext<ServerContext>>,
request: TypedBody<CallRequest>,
) -> Result<HttpResponseOk<TransactionResponse>, HttpError> {
) -> Result<Response<Body>, HttpError> {
let mut state = ctx.context().server_state.lock().await;
let state = state.as_mut().ok_or_else(server_state_error)?;

let call_params = request.into_inner();
let transaction_response = handle_move_call(call_params, state)
.await
.map_err(|err| custom_http_error(StatusCode::BAD_REQUEST, format!("{err}")))?;
Ok(HttpResponseOk(transaction_response))
custom_http_response(StatusCode::OK, transaction_response)
.map_err(|err| custom_http_error(StatusCode::FAILED_DEPENDENCY, format!("{err}")))
}

/**
Expand Down Expand Up @@ -942,7 +952,7 @@ async fn sync(
.await
.map_err(|err| {
custom_http_error(
StatusCode::FAILED_DEPENDENCY,
StatusCode::BAD_REQUEST,
format!("Can't create client state: {err}"),
)
})?;
Expand Down Expand Up @@ -1064,19 +1074,19 @@ async fn get_object_info(
Ok(ObjectRead::Exists(object_ref, object, layout)) => (object_ref, object, layout),
Ok(ObjectRead::Deleted(_)) => {
return Err(custom_http_error(
StatusCode::FAILED_DEPENDENCY,
StatusCode::NOT_FOUND,
format!("Object ({object_id}) was deleted."),
));
}
Ok(ObjectRead::NotExists(_)) => {
return Err(custom_http_error(
StatusCode::FAILED_DEPENDENCY,
StatusCode::NOT_FOUND,
format!("Object ({object_id}) does not exist."),
));
}
Err(error) => {
return Err(custom_http_error(
StatusCode::FAILED_DEPENDENCY,
StatusCode::NOT_FOUND,
format!("Error while getting object info: {:?}", error),
));
}
Expand Down Expand Up @@ -1186,6 +1196,19 @@ async fn handle_move_call(
})
}

fn custom_http_response<T: Serialize + JsonSchema>(
status_code: StatusCode,
response_body: T,
) -> Result<Response<Body>, anyhow::Error> {
let body: Body = serde_json::to_string(&response_body)?.into();
let res = Response::builder()
.status(status_code)
.header(http::header::CONTENT_TYPE, CONTENT_TYPE_JSON)
.header(http::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.body(body)?;
Ok(res)
}

fn custom_http_error(status_code: http::StatusCode, message: String) -> HttpError {
HttpError::for_client_error(None, status_code, message)
}