forked from rojo-rbx/rojo
Upgrade to Tokio 1.x, futures 0.3, Hyper 0.14, etc (#459)
* Upgrade dependencies, oneshot channel ref * New service style? * Fix warning * A server is running again * Working server with async blocks * UI working again * Finish upgrade * Bump MSRV to 1.46.0 for if/match in const fn * Update the README as part of this
This commit is contained in:
committed by
GitHub
parent
4aa5814a0a
commit
5d62bf9b60
@@ -50,7 +50,7 @@ impl BuildCommand {
|
||||
write_model(&session, &self.output, output_kind)?;
|
||||
|
||||
if self.watch {
|
||||
let mut rt = Runtime::new().unwrap();
|
||||
let rt = Runtime::new().unwrap();
|
||||
|
||||
loop {
|
||||
let receiver = session.message_queue().subscribe(cursor);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::sync::{Mutex, RwLock};
|
||||
|
||||
use futures::sync::oneshot;
|
||||
use futures::channel::oneshot;
|
||||
|
||||
/// A message queue with persistent history that can be subscribed to.
|
||||
///
|
||||
|
||||
140
src/web/api.rs
140
src/web/api.rs
@@ -3,9 +3,7 @@
|
||||
|
||||
use std::{collections::HashMap, fs, path::PathBuf, str::FromStr, sync::Arc};
|
||||
|
||||
use futures::{Future, Stream};
|
||||
|
||||
use hyper::{service::Service, Body, Method, Request, StatusCode};
|
||||
use hyper::{body, Body, Method, Request, Response, StatusCode};
|
||||
use rbx_dom_weak::types::Ref;
|
||||
|
||||
use crate::{
|
||||
@@ -21,36 +19,32 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
pub struct ApiService {
|
||||
serve_session: Arc<ServeSession>,
|
||||
pub async fn call(serve_session: Arc<ServeSession>, request: Request<Body>) -> Response<Body> {
|
||||
let service = ApiService::new(serve_session);
|
||||
|
||||
match (request.method(), request.uri().path()) {
|
||||
(&Method::GET, "/api/rojo") => service.handle_api_rojo().await,
|
||||
(&Method::GET, path) if path.starts_with("/api/read/") => {
|
||||
service.handle_api_read(request).await
|
||||
}
|
||||
(&Method::GET, path) if path.starts_with("/api/subscribe/") => {
|
||||
service.handle_api_subscribe(request).await
|
||||
}
|
||||
(&Method::POST, path) if path.starts_with("/api/open/") => {
|
||||
service.handle_api_open(request).await
|
||||
}
|
||||
|
||||
(&Method::POST, "/api/write") => service.handle_api_write(request).await,
|
||||
|
||||
(_method, path) => json(
|
||||
ErrorResponse::not_found(format!("Route not found: {}", path)),
|
||||
StatusCode::NOT_FOUND,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for ApiService {
|
||||
type ReqBody = Body;
|
||||
type ResBody = Body;
|
||||
type Error = hyper::Error;
|
||||
type Future =
|
||||
Box<dyn Future<Item = hyper::Response<Self::ReqBody>, Error = Self::Error> + Send>;
|
||||
|
||||
fn call(&mut self, request: hyper::Request<Self::ReqBody>) -> Self::Future {
|
||||
match (request.method(), request.uri().path()) {
|
||||
(&Method::GET, "/api/rojo") => self.handle_api_rojo(),
|
||||
(&Method::GET, path) if path.starts_with("/api/read/") => self.handle_api_read(request),
|
||||
(&Method::GET, path) if path.starts_with("/api/subscribe/") => {
|
||||
self.handle_api_subscribe(request)
|
||||
}
|
||||
(&Method::POST, path) if path.starts_with("/api/open/") => {
|
||||
self.handle_api_open(request)
|
||||
}
|
||||
|
||||
(&Method::POST, "/api/write") => self.handle_api_write(request),
|
||||
|
||||
(_method, path) => json(
|
||||
ErrorResponse::not_found(format!("Route not found: {}", path)),
|
||||
StatusCode::NOT_FOUND,
|
||||
),
|
||||
}
|
||||
}
|
||||
pub struct ApiService {
|
||||
serve_session: Arc<ServeSession>,
|
||||
}
|
||||
|
||||
impl ApiService {
|
||||
@@ -59,7 +53,7 @@ impl ApiService {
|
||||
}
|
||||
|
||||
/// Get a summary of information about the server
|
||||
fn handle_api_rojo(&self) -> <Self as Service>::Future {
|
||||
async fn handle_api_rojo(&self) -> Response<Body> {
|
||||
let tree = self.serve_session.tree();
|
||||
let root_instance_id = tree.get_root_id();
|
||||
|
||||
@@ -77,7 +71,7 @@ impl ApiService {
|
||||
|
||||
/// Retrieve any messages past the given cursor index, and if
|
||||
/// there weren't any, subscribe to receive any new messages.
|
||||
fn handle_api_subscribe(&self, request: Request<Body>) -> <Self as Service>::Future {
|
||||
async fn handle_api_subscribe(&self, request: Request<Body>) -> Response<Body> {
|
||||
let argument = &request.uri().path()["/api/subscribe/".len()..];
|
||||
let input_cursor: u32 = match argument.parse() {
|
||||
Ok(v) => v,
|
||||
@@ -91,11 +85,15 @@ impl ApiService {
|
||||
|
||||
let session_id = self.serve_session.session_id();
|
||||
|
||||
let receiver = self.serve_session.message_queue().subscribe(input_cursor);
|
||||
let result = self
|
||||
.serve_session
|
||||
.message_queue()
|
||||
.subscribe(input_cursor)
|
||||
.await;
|
||||
|
||||
let tree_handle = self.serve_session.tree_handle();
|
||||
|
||||
Box::new(receiver.then(move |result| match result {
|
||||
match result {
|
||||
Ok((message_cursor, messages)) => {
|
||||
let tree = tree_handle.lock().unwrap();
|
||||
|
||||
@@ -151,56 +149,56 @@ impl ApiService {
|
||||
ErrorResponse::internal_error("Message queue disconnected sender"),
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_api_write(&self, request: Request<Body>) -> <Self as Service>::Future {
|
||||
async fn handle_api_write(&self, request: Request<Body>) -> Response<Body> {
|
||||
let session_id = self.serve_session.session_id();
|
||||
let tree_mutation_sender = self.serve_session.tree_mutation_sender();
|
||||
|
||||
Box::new(request.into_body().concat2().and_then(move |body| {
|
||||
let request: WriteRequest = match serde_json::from_slice(&body) {
|
||||
Ok(request) => request,
|
||||
Err(err) => {
|
||||
return json(
|
||||
ErrorResponse::bad_request(format!("Invalid body: {}", err)),
|
||||
StatusCode::BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
};
|
||||
let body = body::to_bytes(request.into_body()).await.unwrap();
|
||||
|
||||
if request.session_id != session_id {
|
||||
let request: WriteRequest = match serde_json::from_slice(&body) {
|
||||
Ok(request) => request,
|
||||
Err(err) => {
|
||||
return json(
|
||||
ErrorResponse::bad_request("Wrong session ID"),
|
||||
ErrorResponse::bad_request(format!("Invalid body: {}", err)),
|
||||
StatusCode::BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let updated_instances = request
|
||||
.updated
|
||||
.into_iter()
|
||||
.map(|update| PatchUpdate {
|
||||
id: update.id,
|
||||
changed_class_name: update.changed_class_name,
|
||||
changed_name: update.changed_name,
|
||||
changed_properties: update.changed_properties,
|
||||
changed_metadata: None,
|
||||
})
|
||||
.collect();
|
||||
if request.session_id != session_id {
|
||||
return json(
|
||||
ErrorResponse::bad_request("Wrong session ID"),
|
||||
StatusCode::BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
tree_mutation_sender
|
||||
.send(PatchSet {
|
||||
removed_instances: Vec::new(),
|
||||
added_instances: Vec::new(),
|
||||
updated_instances,
|
||||
})
|
||||
.unwrap();
|
||||
let updated_instances = request
|
||||
.updated
|
||||
.into_iter()
|
||||
.map(|update| PatchUpdate {
|
||||
id: update.id,
|
||||
changed_class_name: update.changed_class_name,
|
||||
changed_name: update.changed_name,
|
||||
changed_properties: update.changed_properties,
|
||||
changed_metadata: None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
json_ok(&WriteResponse { session_id })
|
||||
}))
|
||||
tree_mutation_sender
|
||||
.send(PatchSet {
|
||||
removed_instances: Vec::new(),
|
||||
added_instances: Vec::new(),
|
||||
updated_instances,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
json_ok(&WriteResponse { session_id })
|
||||
}
|
||||
|
||||
fn handle_api_read(&self, request: Request<Body>) -> <Self as Service>::Future {
|
||||
async fn handle_api_read(&self, request: Request<Body>) -> Response<Body> {
|
||||
let argument = &request.uri().path()["/api/read/".len()..];
|
||||
let requested_ids: Result<Vec<Ref>, _> = argument.split(',').map(Ref::from_str).collect();
|
||||
|
||||
@@ -239,7 +237,7 @@ impl ApiService {
|
||||
}
|
||||
|
||||
/// Open a script with the given ID in the user's default text editor.
|
||||
fn handle_api_open(&self, request: Request<Body>) -> <Self as Service>::Future {
|
||||
async fn handle_api_open(&self, request: Request<Body>) -> Response<Body> {
|
||||
let argument = &request.uri().path()["/api/open/".len()..];
|
||||
let requested_id = match Ref::from_str(argument) {
|
||||
Ok(id) => id,
|
||||
|
||||
@@ -8,50 +8,19 @@ pub mod interface;
|
||||
mod ui;
|
||||
mod util;
|
||||
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
use std::convert::Infallible;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::{
|
||||
future::{self, FutureResult},
|
||||
Future,
|
||||
use hyper::{
|
||||
server::Server,
|
||||
service::{make_service_fn, service_fn},
|
||||
Body, Request,
|
||||
};
|
||||
use hyper::{service::Service, Body, Request, Response, Server};
|
||||
use log::trace;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use crate::serve_session::ServeSession;
|
||||
|
||||
use self::{api::ApiService, ui::UiService};
|
||||
|
||||
pub struct RootService {
|
||||
api: ApiService,
|
||||
ui: UiService,
|
||||
}
|
||||
|
||||
impl Service for RootService {
|
||||
type ReqBody = Body;
|
||||
type ResBody = Body;
|
||||
type Error = hyper::Error;
|
||||
type Future = Box<dyn Future<Item = Response<Self::ReqBody>, Error = Self::Error> + Send>;
|
||||
|
||||
fn call(&mut self, request: Request<Self::ReqBody>) -> Self::Future {
|
||||
trace!("{} {}", request.method(), request.uri().path());
|
||||
|
||||
if request.uri().path().starts_with("/api") {
|
||||
self.api.call(request)
|
||||
} else {
|
||||
self.ui.call(request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RootService {
|
||||
pub fn new(serve_session: Arc<ServeSession>) -> Self {
|
||||
RootService {
|
||||
api: ApiService::new(Arc::clone(&serve_session)),
|
||||
ui: UiService::new(Arc::clone(&serve_session)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LiveServer {
|
||||
serve_session: Arc<ServeSession>,
|
||||
}
|
||||
@@ -62,14 +31,31 @@ impl LiveServer {
|
||||
}
|
||||
|
||||
pub fn start(self, address: SocketAddr) {
|
||||
let server = Server::bind(&address)
|
||||
.serve(move || {
|
||||
let service: FutureResult<_, hyper::Error> =
|
||||
future::ok(RootService::new(Arc::clone(&self.serve_session)));
|
||||
service
|
||||
})
|
||||
.map_err(|e| eprintln!("Server error: {}", e));
|
||||
let serve_session = Arc::clone(&self.serve_session);
|
||||
|
||||
hyper::rt::run(server);
|
||||
let make_service = make_service_fn(move |_conn| {
|
||||
let serve_session = Arc::clone(&serve_session);
|
||||
|
||||
async {
|
||||
let service = move |req: Request<Body>| {
|
||||
let serve_session = Arc::clone(&serve_session);
|
||||
|
||||
async move {
|
||||
if req.uri().path().starts_with("/api") {
|
||||
Ok::<_, Infallible>(api::call(serve_session, req).await)
|
||||
} else {
|
||||
Ok::<_, Infallible>(ui::call(serve_session, req).await)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok::<_, Infallible>(service_fn(service))
|
||||
}
|
||||
});
|
||||
|
||||
let rt = Runtime::new().unwrap();
|
||||
let _guard = rt.enter();
|
||||
let server = Server::bind(&address).serve(make_service);
|
||||
rt.block_on(server).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
use std::{borrow::Cow, sync::Arc, time::Duration};
|
||||
|
||||
use futures::{future, Future};
|
||||
use hyper::{header, service::Service, Body, Method, Request, Response, StatusCode};
|
||||
use hyper::{header, Body, Method, Request, Response, StatusCode};
|
||||
use maplit::hashmap;
|
||||
use rbx_dom_weak::types::{Ref, Variant};
|
||||
use ritz::{html, Fragment, HtmlContent, HtmlSelfClosingTag};
|
||||
@@ -22,32 +21,23 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
pub struct UiService {
|
||||
serve_session: Arc<ServeSession>,
|
||||
pub async fn call(serve_session: Arc<ServeSession>, request: Request<Body>) -> Response<Body> {
|
||||
let service = UiService::new(serve_session);
|
||||
|
||||
match (request.method(), request.uri().path()) {
|
||||
(&Method::GET, "/") => service.handle_home(),
|
||||
(&Method::GET, "/logo.png") => service.handle_logo(),
|
||||
(&Method::GET, "/icon.png") => service.handle_icon(),
|
||||
(&Method::GET, "/show-instances") => service.handle_show_instances(),
|
||||
(_method, path) => json(
|
||||
ErrorResponse::not_found(format!("Route not found: {}", path)),
|
||||
StatusCode::NOT_FOUND,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for UiService {
|
||||
type ReqBody = Body;
|
||||
type ResBody = Body;
|
||||
type Error = hyper::Error;
|
||||
type Future = Box<dyn Future<Item = Response<Self::ReqBody>, Error = Self::Error> + Send>;
|
||||
|
||||
fn call(&mut self, request: Request<Self::ReqBody>) -> Self::Future {
|
||||
let response = match (request.method(), request.uri().path()) {
|
||||
(&Method::GET, "/") => self.handle_home(),
|
||||
(&Method::GET, "/logo.png") => self.handle_logo(),
|
||||
(&Method::GET, "/icon.png") => self.handle_icon(),
|
||||
(&Method::GET, "/show-instances") => self.handle_show_instances(),
|
||||
(_method, path) => {
|
||||
return json(
|
||||
ErrorResponse::not_found(format!("Route not found: {}", path)),
|
||||
StatusCode::NOT_FOUND,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
Box::new(future::ok(response))
|
||||
}
|
||||
pub struct UiService {
|
||||
serve_session: Arc<ServeSession>,
|
||||
}
|
||||
|
||||
impl UiService {
|
||||
|
||||
@@ -1,23 +1,11 @@
|
||||
use futures::{future, Future};
|
||||
use hyper::{header::CONTENT_TYPE, Body, Response, StatusCode};
|
||||
use serde::Serialize;
|
||||
|
||||
/// Respond to a request with JSON and the given status code.
|
||||
pub fn json<T: Serialize>(
|
||||
value: T,
|
||||
code: StatusCode,
|
||||
) -> Box<dyn Future<Item = hyper::Response<hyper::Body>, Error = hyper::Error> + Send> {
|
||||
Box::new(future::ok(response_json(value, code)))
|
||||
}
|
||||
|
||||
/// Respond to a request with a 200 OK response containing JSON.
|
||||
pub fn json_ok<T: Serialize>(
|
||||
value: T,
|
||||
) -> Box<dyn Future<Item = hyper::Response<hyper::Body>, Error = hyper::Error> + Send> {
|
||||
pub fn json_ok<T: Serialize>(value: T) -> Response<Body> {
|
||||
json(value, StatusCode::OK)
|
||||
}
|
||||
|
||||
fn response_json<T: Serialize>(value: T, code: StatusCode) -> Response<Body> {
|
||||
pub fn json<T: Serialize>(value: T, code: StatusCode) -> Response<Body> {
|
||||
let serialized = match serde_json::to_string(&value) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
|
||||
Reference in New Issue
Block a user