-
Notifications
You must be signed in to change notification settings - Fork 55
/
server.rs
152 lines (132 loc) · 5.53 KB
/
server.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use std::time::Duration;
use axum::body::{Body, Bytes};
use axum::extract::{DefaultBodyLimit, Request};
use axum::http::{HeaderValue, StatusCode};
use axum::middleware::Next;
use axum::response::{IntoResponse, Response};
use axum::routing::{get, post, IntoMakeService};
use axum::Router;
use http_body_util::BodyExt;
use reqwest::{header, Method};
use starknet_core::starknet::starknet_config::StarknetConfig;
use tokio::net::TcpListener;
use tower_http::cors::CorsLayer;
use tower_http::timeout::TimeoutLayer;
use tower_http::trace::TraceLayer;
use crate::api::http::{endpoints as http, HttpApiHandler};
use crate::api::json_rpc::origin_forwarder::OriginForwarder;
use crate::api::json_rpc::JsonRpcHandler;
use crate::api::Api;
use crate::rpc_handler::RpcHandler;
use crate::{rpc_handler, ServerConfig};
pub type StarknetDevnetServer = axum::serve::Serve<IntoMakeService<Router>, Router>;
fn json_rpc_routes<TJsonRpcHandler: RpcHandler>(json_rpc_handler: TJsonRpcHandler) -> Router {
Router::new()
.route("/", post(rpc_handler::handle::<TJsonRpcHandler>))
.route("/rpc", post(rpc_handler::handle::<TJsonRpcHandler>))
.with_state(json_rpc_handler)
}
fn http_api_routes(http_api_handler: HttpApiHandler) -> Router {
Router::new()
.route("/is_alive", get(http::is_alive))
.route("/dump", post(http::dump_load::dump))
.route("/load", post(http::dump_load::load))
.route("/postman/load_l1_messaging_contract", post(http::postman::postman_load))
.route("/postman/flush", post(http::postman::postman_flush))
.route("/postman/send_message_to_l2", post(http::postman::postman_send_message_to_l2))
.route(
"/postman/consume_message_from_l2",
post(http::postman::postman_consume_message_from_l2),
)
.route("/create_block", post(http::blocks::create_block))
.route("/abort_blocks", post(http::blocks::abort_blocks))
.route("/restart", post(http::restart))
.route("/set_time", post(http::time::set_time))
.route("/increase_time", post(http::time::increase_time))
.route("/predeployed_accounts", get(http::accounts::get_predeployed_accounts))
.route("/account_balance", get(http::accounts::get_account_balance))
.route("/mint", post(http::mint_token::mint))
.route("/config", get(http::get_devnet_config))
.with_state(http_api_handler)
}
/// Configures an [axum::Server] that handles related JSON-RPC calls and WEB API calls via HTTP
pub fn serve_http_api_json_rpc(
tcp_listener: TcpListener,
api: Api,
starknet_config: &StarknetConfig,
server_config: &ServerConfig,
) -> StarknetDevnetServer {
let http_handler = HttpApiHandler { api: api.clone(), server_config: server_config.clone() };
let origin_caller = if let (Some(url), Some(block_number)) =
(&starknet_config.fork_config.url, starknet_config.fork_config.block_number)
{
Some(OriginForwarder::new(url.to_string(), block_number))
} else {
None
};
let json_rpc_handler =
JsonRpcHandler { api, origin_caller, server_config: server_config.clone() };
let json_rpc_routes = json_rpc_routes(json_rpc_handler);
let http_api_routes = http_api_routes(http_handler);
let mut routes = http_api_routes.merge(json_rpc_routes).layer(TraceLayer::new_for_http());
if server_config.log_response {
routes = routes.layer(axum::middleware::from_fn(response_logging_middleware));
};
routes = routes
.layer(TimeoutLayer::new(Duration::from_secs(server_config.timeout.into())))
.layer(DefaultBodyLimit::max(server_config.request_body_size_limit))
.layer(
CorsLayer::new()
// More details: https://docs.rs/tower-http/latest/tower_http/cors/index.html
.allow_origin("*".parse::<HeaderValue>().unwrap())
.allow_headers(vec![header::CONTENT_TYPE])
.allow_methods(vec![Method::GET, Method::POST]),
);
if server_config.log_request {
routes = routes.layer(axum::middleware::from_fn(request_logging_middleware));
}
axum::serve(tcp_listener, routes.into_make_service())
}
async fn log_body_and_path<T>(
body: T,
uri_option: Option<axum::http::Uri>,
) -> Result<axum::body::Body, (StatusCode, String)>
where
T: axum::body::HttpBody<Data = Bytes>,
T::Error: std::fmt::Display,
{
let bytes = match body.collect().await {
Ok(collected) => collected.to_bytes(),
Err(err) => {
return Err((StatusCode::INTERNAL_SERVER_ERROR, err.to_string()));
}
};
if let Ok(body_str) = std::str::from_utf8(&bytes) {
if let Some(uri) = uri_option {
tracing::info!("{} {}", uri, body_str);
} else {
tracing::info!("{}", body_str);
}
} else {
tracing::error!("Failed to convert body to string");
}
Ok(Body::from(bytes))
}
async fn request_logging_middleware(
request: Request,
next: Next,
) -> Result<impl IntoResponse, (StatusCode, String)> {
let (parts, body) = request.into_parts();
let body = log_body_and_path(body, Some(parts.uri.clone())).await?;
Ok(next.run(Request::from_parts(parts, body)).await)
}
async fn response_logging_middleware(
request: Request,
next: Next,
) -> Result<impl IntoResponse, (StatusCode, String)> {
let response = next.run(request).await;
let (parts, body) = response.into_parts();
let body = log_body_and_path(body, None).await?;
let response = Response::from_parts(parts, body);
Ok(response)
}