From 57d46287d7b396839b4893db1dfcea3fd5b4087c Mon Sep 17 00:00:00 2001 From: Lucien Greathouse Date: Mon, 9 Sep 2019 18:32:44 -0700 Subject: [PATCH] Glue message queue onto ServeSession, simplify some HTTP --- src/message_queue.rs | 2 +- src/serve_session.rs | 11 +++++++- src/web/api.rs | 60 ++++++++++++++++++++++++++------------------ src/web/interface.rs | 21 ++++++++++++---- src/web/ui.rs | 13 ++++++---- src/web/util.rs | 17 ++++++++++++- 6 files changed, 87 insertions(+), 37 deletions(-) diff --git a/src/message_queue.rs b/src/message_queue.rs index 2ae52b28..2eb22609 100644 --- a/src/message_queue.rs +++ b/src/message_queue.rs @@ -77,7 +77,7 @@ impl MessageQueue { message_listeners.push(listener); } - pub fn get_message_cursor(&self) -> u32 { + pub fn cursor(&self) -> u32 { self.messages.read().unwrap().len() as u32 } diff --git a/src/serve_session.rs b/src/serve_session.rs index 1396b1de..8d132261 100644 --- a/src/serve_session.rs +++ b/src/serve_session.rs @@ -1,22 +1,27 @@ use std::collections::HashSet; -use crate::{project::Project, session_id::SessionId, snapshot::RojoTree}; +use crate::{ + message_queue::MessageQueue, project::Project, session_id::SessionId, snapshot::RojoTree, +}; /// Contains all of the state for a Rojo serve session. pub struct ServeSession { root_project: Option, session_id: SessionId, tree: RojoTree, + message_queue: MessageQueue<()>, // TODO: Real message type } impl ServeSession { pub fn new(tree: RojoTree, root_project: Option) -> ServeSession { let session_id = SessionId::new(); + let message_queue = MessageQueue::new(); ServeSession { session_id, root_project, tree, + message_queue, } } @@ -24,6 +29,10 @@ impl ServeSession { &self.tree } + pub fn message_queue(&self) -> &MessageQueue<()> { + &self.message_queue + } + pub fn session_id(&self) -> SessionId { self.session_id } diff --git a/src/web/api.rs b/src/web/api.rs index 03962d03..de88c73a 100644 --- a/src/web/api.rs +++ b/src/web/api.rs @@ -1,7 +1,7 @@ //! Defines Rojo's HTTP API, all under /api. These endpoints generally return //! JSON. -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use futures::{future, Future}; @@ -12,9 +12,10 @@ use crate::{ serve_session::ServeSession, web::{ interface::{ - ReadResponse, ServerInfoResponse, SubscribeResponse, PROTOCOL_VERSION, SERVER_VERSION, + NotFoundError, ReadResponse, ServerInfoResponse, SubscribeResponse, PROTOCOL_VERSION, + SERVER_VERSION, }, - util::response_json, + util::{json, json_ok}, }, }; @@ -30,19 +31,14 @@ impl Service for ApiService { Box, Error = Self::Error> + Send>; fn call(&mut self, request: hyper::Request) -> Self::Future { - let response = match (request.method(), request.uri().path()) { + 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/") => { - return self.handle_api_subscribe(request); + self.handle_api_subscribe(request) } - _ => Response::builder() - .status(StatusCode::NOT_FOUND) - .body(Body::empty()) - .unwrap(), - }; - - Box::new(future::ok(response)) + _ => json(NotFoundError, StatusCode::NOT_FOUND), + } } } @@ -52,11 +48,11 @@ impl ApiService { } /// Get a summary of information about the server - fn handle_api_rojo(&self) -> Response { + fn handle_api_rojo(&self) -> ::Future { let tree = self.serve_session.tree(); let root_instance_id = tree.get_root_id(); - response_json(&ServerInfoResponse { + json_ok(&ServerInfoResponse { server_version: SERVER_VERSION.to_owned(), protocol_version: PROTOCOL_VERSION, session_id: self.serve_session.session_id(), @@ -69,7 +65,7 @@ impl ApiService { /// there weren't any, subscribe to receive any new messages. fn handle_api_subscribe(&self, request: Request) -> ::Future { let argument = &request.uri().path()["/api/subscribe/".len()..]; - let _cursor: u32 = match argument.parse() { + let _input_cursor: u32 = match argument.parse() { Ok(v) => v, Err(err) => { return Box::new(future::ok( @@ -82,28 +78,44 @@ impl ApiService { } }; - Box::new(future::ok(response_json(SubscribeResponse { + let message_queue = self.serve_session.message_queue(); + let message_cursor = message_queue.cursor(); + + let messages = Vec::new(); // TODO + + json_ok(SubscribeResponse { session_id: self.serve_session.session_id(), - }))) + message_cursor, + messages, + }) } - fn handle_api_read(&self, request: Request) -> Response { + fn handle_api_read(&self, request: Request) -> ::Future { let argument = &request.uri().path()["/api/read/".len()..]; let requested_ids: Option> = argument.split(',').map(RbxId::parse_str).collect(); let _requested_ids = match requested_ids { Some(id) => id, None => { - return Response::builder() - .status(StatusCode::BAD_REQUEST) - .header(header::CONTENT_TYPE, "text/plain") - .body(Body::from("Malformed ID list")) - .unwrap(); + return Box::new(future::ok( + Response::builder() + .status(StatusCode::BAD_REQUEST) + .header(header::CONTENT_TYPE, "text/plain") + .body(Body::from("Malformed ID list")) + .unwrap(), + )); } }; - response_json(ReadResponse { + let message_queue = self.serve_session.message_queue(); + let message_cursor = message_queue.cursor(); + + let instances = HashMap::new(); // TODO + + json_ok(ReadResponse { session_id: self.serve_session.session_id(), + message_cursor, + instances, }) } } diff --git a/src/web/interface.rs b/src/web/interface.rs index 7f8d5d82..162f9395 100644 --- a/src/web/interface.rs +++ b/src/web/interface.rs @@ -1,6 +1,6 @@ //! Defines all the structs needed to interact with the Rojo Serve API. -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use rbx_dom_weak::RbxId; use serde::{Deserialize, Serialize}; @@ -13,6 +13,14 @@ pub(crate) const SERVER_VERSION: &str = env!("CARGO_PKG_VERSION"); /// Current protocol version, which is required to match. pub const PROTOCOL_VERSION: u64 = 3; +// TODO +#[derive(Debug, Serialize, Deserialize)] +pub struct SubscribeMessage; + +// TODO +#[derive(Debug, Serialize, Deserialize)] +pub struct Instance; + /// Response body from /api/rojo #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -29,8 +37,8 @@ pub struct ServerInfoResponse { #[serde(rename_all = "camelCase")] pub struct ReadResponse { pub session_id: SessionId, - // pub message_cursor: u32, - // pub instances: HashMap>, + pub message_cursor: u32, + pub instances: HashMap, } /// Response body from /api/subscribe/{cursor} @@ -38,6 +46,9 @@ pub struct ReadResponse { #[serde(rename_all = "camelCase")] pub struct SubscribeResponse { pub session_id: SessionId, - // pub message_cursor: u32, - // pub messages: Cow<'a, [InstanceChanges]>, + pub message_cursor: u32, + pub messages: Vec, } + +#[derive(Debug, Serialize, Deserialize)] +pub struct NotFoundError; diff --git a/src/web/ui.rs b/src/web/ui.rs index 01471839..4212f21a 100644 --- a/src/web/ui.rs +++ b/src/web/ui.rs @@ -6,7 +6,13 @@ use futures::{future, Future}; use hyper::{header, service::Service, Body, Method, Request, Response, StatusCode}; use ritz::html; -use crate::{serve_session::ServeSession, web::interface::SERVER_VERSION}; +use crate::{ + serve_session::ServeSession, + web::{ + interface::{NotFoundError, SERVER_VERSION}, + util::json, + }, +}; static HOME_CSS: &str = include_str!("../../assets/index.css"); @@ -26,10 +32,7 @@ impl Service for UiService { (&Method::GET, "/") => self.handle_home(), (&Method::GET, "/visualize/rbx") => self.handle_visualize_rbx(), (&Method::GET, "/visualize/imfs") => self.handle_visualize_imfs(), - _ => Response::builder() - .status(StatusCode::NOT_FOUND) - .body(Body::empty()) - .unwrap(), + _ => return json(NotFoundError, StatusCode::NOT_FOUND), }; Box::new(future::ok(response)) diff --git a/src/web/util.rs b/src/web/util.rs index 32581c38..b882b372 100644 --- a/src/web/util.rs +++ b/src/web/util.rs @@ -1,7 +1,8 @@ +use futures::{future, Future}; use hyper::{header::CONTENT_TYPE, Body, Response, StatusCode}; use serde::Serialize; -pub fn response_json(value: T) -> Response { +fn response_json(value: T, code: StatusCode) -> Response { let serialized = match serde_json::to_string(&value) { Ok(v) => v, Err(err) => { @@ -14,7 +15,21 @@ pub fn response_json(value: T) -> Response { }; Response::builder() + .status(code) .header(CONTENT_TYPE, "application/json") .body(Body::from(serialized)) .unwrap() } + +pub fn json( + value: T, + code: StatusCode, +) -> Box, Error = hyper::Error> + Send> { + Box::new(future::ok(response_json(value, code))) +} + +pub fn json_ok( + value: T, +) -> Box, Error = hyper::Error> + Send> { + json(value, StatusCode::OK) +}