makepad_hub/
httpserver.rs1use std::net::{TcpListener, TcpStream, SocketAddr, Shutdown};
2use std::sync::{mpsc, Arc, Mutex};
3use std::io::prelude::*;
4use std::io::BufReader;
5use std::str;
6use std::time::Duration;
7use std::collections::HashMap;
8use serde::{Serialize, Deserialize};
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
11pub enum HttpServerConfig {
12 Offline,
13 Localhost(u16),
14 Network(u16),
15 InterfaceV4((u16, [u8; 4]))
16}
17
18#[derive(Default)]
19pub struct HttpServerShared {
20 pub terminate: bool,
21 pub watcher_id: u64,
22 pub watch_pending: Vec<(u64, mpsc::Sender<String>)>,
23 pub files_read: Vec<String>,
24}
25
26#[derive(Default)]
27pub struct HttpServer {
28 pub listen_thread: Option<std::thread::JoinHandle<()>>,
29 pub listen_address: Option<SocketAddr>,
30 pub shared: Arc<Mutex<HttpServerShared>>,
31}
32
33impl HttpServer {
34 pub fn start_http_server(config: &HttpServerConfig, workspaces_arc: Arc<Mutex<HashMap<String, String>>>) -> Option<HttpServer> {
35
36 let listen_address = match config {
37 HttpServerConfig::Offline => return None,
38 HttpServerConfig::Localhost(port) => SocketAddr::from(([127, 0, 0, 1], *port)),
39 HttpServerConfig::Network(port) => SocketAddr::from(([0, 0, 0, 0], *port)),
40 HttpServerConfig::InterfaceV4((port, ip)) => SocketAddr::from((*ip, *port)),
41 };
42
43 let listener = if let Ok(listener) = TcpListener::bind(listen_address.clone()) {listener} else {println!("Cannot bind http server port"); return None};
44 let workspaces = Arc::clone(&workspaces_arc);
45 let shared = Arc::new(Mutex::new(HttpServerShared::default()));
46
47 let listen_thread = {
48 let shared = Arc::clone(&shared);
49 std::thread::spawn(move || {
50 for tcp_stream in listener.incoming() {
51 if let Ok(shared) = shared.lock() {
52 if shared.terminate {
53 return
54 }
55 }
56 let mut tcp_stream = tcp_stream.expect("Incoming stream failure");
57 let (tx_write, rx_write) = mpsc::channel::<String>();
58 let mut reader = BufReader::new(tcp_stream.try_clone().expect("Cannot clone tcp stream"));
59 let workspaces = Arc::clone(&workspaces);
60 let shared = Arc::clone(&shared);
61 let _read_thread = std::thread::spawn(move || {
62
63 let mut line = String::new();
64 reader.read_line(&mut line).expect("http read line fail");
65 if !line.starts_with("GET /") || line.len() < 10 {
66 let _ = tcp_stream.shutdown(Shutdown::Both);
67 return
68 }
69
70 let line = &line[5..];
71 let space = line.find(' ').expect("http space fail");
72 let mut url = line[0..space].to_string();
73 if url.ends_with("/"){
74 url.push_str("index.html");
75 }
76 let url_lc = url.clone();
77 url_lc.to_lowercase();
78 if url_lc.ends_with("/key.ron") || url.find("..").is_some() || url.starts_with("/") {
79 let _ = tcp_stream.shutdown(Shutdown::Both);
80 return
81 }
82 if url_lc.starts_with("$watch") { let mut watcher_id = 0;
84 if let Ok(mut shared) = shared.lock() {
85 shared.watcher_id += 1;
86 watcher_id = shared.watcher_id;
87 shared.watch_pending.push((watcher_id, tx_write));
88 };
89 match rx_write.recv_timeout(Duration::from_secs(30)) {
90 Ok(msg) => { write_bytes_to_tcp_stream_no_error(&mut tcp_stream, msg.as_bytes());
92 let _ = tcp_stream.shutdown(Shutdown::Both);
93 },
94 Err(_) => { write_bytes_to_tcp_stream_no_error(&mut tcp_stream, "HTTP/1.1 201 Retry\r\n\r\n".as_bytes());
96 let _ = tcp_stream.shutdown(Shutdown::Both);
97 }
98 }
99
100 if let Ok(mut shared) = shared.lock() {
101 for i in 0..shared.watch_pending.len() {
102 let (id, _) = &shared.watch_pending[i];
103 if *id == watcher_id {
104 shared.watch_pending.remove(i);
105 break
106 }
107 }
108 };
109 return
110 }
111
112 if url.ends_with("favicon.ico"){
113 let header = "HTTP/1.1 200 OK\r\nContent-Type: image/x-icon\r\nTransfer-encoding: identity\r\nContent-Length: 0\r\n: close\r\n\r\n";
114 write_bytes_to_tcp_stream_no_error(&mut tcp_stream, header.as_bytes());
115 let _ = tcp_stream.shutdown(Shutdown::Both);
116 return
117 }
118
119 let file_path = if let Some(file_pos) = url.find('/') {
120 let (workspace, rest) = url.split_at(file_pos);
121 let (_, rest) = rest.split_at(1);
122 if let Ok(workspaces) = workspaces.lock() {
123 if let Some(abs_path) = workspaces.get(workspace) {
124 Some(format!("{}/{}", abs_path, rest))
125 }
126 else {None}
127 }
128 else {None}
129 }
130 else {None};
131
132 if file_path.is_none() {
133 let _ = tcp_stream.shutdown(Shutdown::Both);
134 return
135 }
136 let file_path = file_path.unwrap();
137 let file_path = if file_path.ends_with("/"){
138 format!("{}/{}", file_path, "index.html")
139 }
140 else{
141 file_path
142 };
143
144 if let Ok(mut shared) = shared.lock() {
145 if shared.files_read.iter().find( | v | **v == url).is_none() {
146 shared.files_read.push(url.to_string());
147 }
148 };
149
150 if let Ok(data) = std::fs::read(&file_path) {
151 let mime_type = if url.ends_with(".html") {"text/html"}
152 else if url.ends_with(".wasm") {"application/wasm"}
153 else if url.ends_with(".js") {"text/javascript"}
154 else {"application/octet-stream"};
155
156 let header = format!(
158 "HTTP/1.1 200 OK\r\nContent-Type: {}\r\nContent-encoding: identity\r\nTransfer-encoding: identity\r\nContent-Length: {}\r\nConnection: close\r\n\r\n",
159 mime_type,
160 data.len()
161 );
162 write_bytes_to_tcp_stream_no_error(&mut tcp_stream, header.as_bytes());
163 write_bytes_to_tcp_stream_no_error(&mut tcp_stream, &data);
164 let _ = tcp_stream.shutdown(Shutdown::Both);
165 }
166 else { write_bytes_to_tcp_stream_no_error(&mut tcp_stream, "HTTP/1.1 404 NotFound\r\n".as_bytes());
168 let _ = tcp_stream.shutdown(Shutdown::Both);
169 }
170 });
171 }
172 })
173 };
174 Some(HttpServer {
175 listen_thread: Some(listen_thread),
176 listen_address: Some(listen_address.clone()),
177 shared: shared,
178 })
179 }
180
181 pub fn send_json_message(&mut self, json_msg: &str) {
182 if let Ok(shared) = self.shared.lock() {
183 for (_, tx) in &shared.watch_pending {
184 let msg = format!(
185 "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-encoding: identity\r\nTransfer-encoding: identity\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
186 json_msg.len(),
187 json_msg
188 );
189 let _ = tx.send(msg);
190 }
191 }
192 }
193
194 pub fn send_file_change(&mut self, path: &str) {
195 if let Ok(shared) = self.shared.lock() {
196 if shared.files_read.iter().find( | v | **v == path).is_none() {
197 return
198 }
199 }
200 self.send_json_message(&format!("{{\"type\":\"file_change\",\"path\":\"{}\"}}", path));
201 }
202
203 pub fn send_build_start(&mut self) {
204 }
206
207 pub fn terminate(&mut self) {
208 if let Ok(mut shared) = self.shared.lock() {
209 shared.terminate = true;
210 for (_, tx) in &shared.watch_pending {
211 let _ = tx.send("HTTP/1.1 201 Retry\r\n\r\n".to_string());
212 }
213 }
214 if let Some(listen_address) = self.listen_address {
215 self.listen_address = None;
216 if let Ok(_) = TcpStream::connect(listen_address) {
217 self.listen_thread.take().expect("cant take listen thread").join().expect("cant join listen thread");
218 }
219 }
220 }
221}
222
223fn write_bytes_to_tcp_stream_no_error(tcp_stream: &mut TcpStream, bytes: &[u8]) {
224 let bytes_total = bytes.len();
225 let mut bytes_left = bytes_total;
226 while bytes_left > 0 {
227 let buf = &bytes[(bytes_total - bytes_left)..bytes_total];
228 if let Ok(bytes_written) = tcp_stream.write(buf) {
229 if bytes_written == 0 {
230 return
231 }
232 bytes_left -= bytes_written;
233 }
234 else {
235 return
236 }
237 }
238}