Skip to content

Commit 8df71da

Browse files
committed
Move rendering to a web server
1 parent 9a542e0 commit 8df71da

22 files changed

Lines changed: 1695 additions & 2093 deletions

File tree

Cargo.lock

Lines changed: 104 additions & 1353 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ env_logger = "0.10"
1010
log = "0.4.17"
1111
serde = { version = "1.0.140", features = ["derive"] }
1212
ansi-to-html = { path = "ansi-to-html" }
13-
color-eyre = "0.6.2"
1413
semver = "1.0.12"
1514
tar = "0.4.38"
1615
flate2 = "1.0.24"
@@ -22,14 +21,12 @@ regex = "1.6.0"
2221
uuid = { version = "1.1.2", features = ["v4"] }
2322
num_cpus = "1.13.1"
2423
tokio = { version = "1.21.2", features = ["full"] }
25-
futures-util = "0.3.24"
26-
aws-sdk-s3 = "0.24"
27-
aws-smithy-types-convert = { version = "0.54", features = ["convert-time"] }
28-
aws-config = "0.54"
2924
serde_json = "1.0.96"
3025
xz2 = "0.1.7"
31-
backoff = { version = "0.4.0", features = ["futures", "tokio"] }
26+
backoff = "0.4.0"
3227
time = { version = "0.3", features = ["std"] }
28+
anyhow = { version = "1.0.80", features = ["backtrace"] }
29+
ssh2 = "0.9.4"
3330

3431
[profile.release]
3532
debug = 1
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
#![no_main]
22

33
use libfuzzer_sys::fuzz_target;
4+
use std::io::Write;
5+
use ansi_to_html::Renderer;
46

57
fuzz_target!(|data: &[u8]| {
6-
let _ = ansi_to_html::render(String::new(), data);
8+
let mut renderer = Renderer::new(data);
9+
while let Some(line) = renderer.next_line().unwrap() {
10+
std::io::sink().write_all(&line).unwrap();
11+
}
12+
assert!(matches!(renderer.next_line(), Ok(None)));
713
});

ansi-to-html/src/lib.rs

Lines changed: 90 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,109 @@
1-
use std::io::Write;
1+
use std::io::Read;
22

33
mod ansi;
44
mod perform;
55
mod renderer;
66

7-
pub struct Handle {
8-
renderer: renderer::Renderer,
7+
pub struct Renderer<R> {
8+
inner: renderer::Renderer,
99
parser: vte::Parser,
10+
bytes: R,
11+
state: State,
12+
title: String,
1013
}
1114

12-
impl Handle {
13-
pub fn new() -> Self {
15+
#[rustfmt::skip]
16+
macro_rules! log_format {
17+
() => {
18+
r#"<html><head><style>
19+
body {{
20+
background: #111;
21+
color: #eee;
22+
}}
23+
pre {{
24+
word-wrap: break-word;
25+
white-space: pre-wrap;
26+
font-size: 14px;
27+
font-size-adjust: none;
28+
text-size-adjust: none;
29+
-webkit-text-size-adjust: 100%;
30+
-moz-text-size-adjust: 100%;
31+
-ms-text-size-adjust: 100%;
32+
}}
33+
</style><title>{}</title></head>
34+
<script>
35+
function scroll_to_ub() {{
36+
var ub = document.getElementById("ub");
37+
if (ub !== null) {{
38+
ub.scrollIntoView();
39+
}}
40+
}}
41+
</script>
42+
<body onload="scroll_to_ub()">
43+
<pre style="text-align: center;">{}</pre>
44+
<pre>"#
45+
}
46+
}
47+
48+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49+
enum State {
50+
Fresh,
51+
Rendering,
52+
Done,
53+
}
54+
55+
impl<R: Read> Renderer<R> {
56+
pub fn new(bytes: R, title: String) -> Self {
1457
Self {
15-
renderer: renderer::Renderer::new(String::new()),
58+
inner: renderer::Renderer::new(String::new()),
1659
parser: vte::Parser::new(),
60+
bytes,
61+
state: State::Fresh,
62+
title,
1763
}
1864
}
1965

20-
pub fn finish<F: Write>(&self, mut output: F) -> std::io::Result<()> {
21-
output.write_all(
22-
br#"<!DOCTYPE html><html><body style="background:#111;color:#eee;"><body>
23-
<pre style="word-wrap:break-word;white-space:pre-wrap;font-size:14px;font-size-adjust:none;text-size-adjust:none;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;"></style>"#
24-
)?;
25-
self.renderer.emit_html(&mut output)?;
26-
output.write_all(b"</pre></body><style>")?;
27-
self.renderer.emit_css(&mut output)?;
28-
output.write_all(b"</style></html>")
66+
pub fn is_empty(&self) -> bool {
67+
self.state == State::Done
2968
}
30-
}
3169

32-
impl Write for Handle {
33-
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
34-
for b in buf {
35-
self.parser.advance(&mut self.renderer, *b);
36-
}
37-
Ok(buf.len())
70+
fn first_line(&self) -> Vec<u8> {
71+
format!(log_format!(), self.title, self.title).into_bytes()
3872
}
3973

40-
fn flush(&mut self) -> std::io::Result<()> {
41-
Ok(())
74+
pub fn next_line(&mut self) -> Result<Option<Vec<u8>>, std::io::Error> {
75+
match self.state {
76+
State::Done => return Ok(None),
77+
State::Fresh => {
78+
self.state = State::Rendering;
79+
return Ok(Some(self.first_line()));
80+
}
81+
State::Rendering => {}
82+
}
83+
loop {
84+
let mut byte = [0u8];
85+
let n = self.bytes.read(&mut byte)?;
86+
if n == 0 {
87+
let line = if let Some(line) = self.inner.remove_oldest_row() {
88+
Some(line)
89+
} else {
90+
self.state = State::Done;
91+
let mut line = Vec::new();
92+
line.extend(b"</span></pre></body><style>");
93+
self.inner.emit_css(&mut line)?;
94+
line.extend(b"</style></html>");
95+
line.into()
96+
};
97+
return Ok(line);
98+
}
99+
self.parser.advance(&mut self.inner, byte[0]);
100+
if let Some(mut line) = self.inner.pop_completed_row() {
101+
if line.is_empty() {
102+
log::warn!("renderer produced an empty line!");
103+
line = b"\n".to_vec();
104+
}
105+
return Ok(Some(line));
106+
}
107+
}
42108
}
43109
}
44-
45-
pub fn render(name: String, bytes: &[u8]) -> (String, String) {
46-
let mut h = Handle {
47-
renderer: renderer::Renderer::new(name),
48-
parser: vte::Parser::new(),
49-
};
50-
h.write_all(&bytes).unwrap();
51-
let mut html = Vec::new();
52-
h.renderer.emit_html(&mut html).unwrap();
53-
let mut css = Vec::new();
54-
h.renderer.emit_css(&mut css).unwrap();
55-
56-
(
57-
String::from_utf8(css).unwrap(),
58-
String::from_utf8(html).unwrap(),
59-
)
60-
}

ansi-to-html/src/main.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
use std::io::{copy, stdin, stdout};
1+
use std::io::{stdin, stdout, Write};
22

3-
fn main() {
3+
fn main() -> Result<(), std::io::Error> {
44
env_logger::init();
5-
let mut handle = ansi_to_html::Handle::new();
6-
copy(&mut stdin().lock(), &mut handle).unwrap();
7-
handle.finish(stdout().lock()).unwrap();
5+
let mut renderer = ansi_to_html::Renderer::new(stdin().lock(), String::from("stdin"));
6+
let mut out = stdout().lock();
7+
while let Some(line) = renderer.next_line()? {
8+
out.write_all(&line)?;
9+
}
10+
Ok(())
811
}

ansi-to-html/src/renderer.rs

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::ansi::Color;
22
use std::cell::RefCell;
33
use std::collections::HashMap;
4+
use std::collections::VecDeque;
45

56
pub struct Renderer {
67
pub name: String,
@@ -11,7 +12,7 @@ pub struct Renderer {
1112
pub foreground: Color,
1213
pub background: Color,
1314
current_row: usize,
14-
rows: Vec<Row>,
15+
rows: VecDeque<Row>,
1516
styles: Styles,
1617
}
1718

@@ -112,7 +113,7 @@ impl Renderer {
112113
foreground: Color::bright_white(),
113114
background: Color::black(),
114115
current_row: 0,
115-
rows: vec![Row::new()],
116+
rows: vec![Row::new()].into(),
116117
styles: Styles::default(),
117118
}
118119
}
@@ -152,7 +153,7 @@ impl Renderer {
152153
pub fn linefeed(&mut self) {
153154
self.current_row += 1;
154155
if self.current_row == self.rows.len() {
155-
self.rows.push(Row::new());
156+
self.rows.push_back(Row::new());
156157
}
157158
}
158159

@@ -185,7 +186,7 @@ impl Renderer {
185186
pub fn handle_move(&mut self, row: u16, col: u16) {
186187
self.current_row = row as usize;
187188
while self.current_row >= self.rows.len() {
188-
self.rows.push(Row::new());
189+
self.rows.push_back(Row::new());
189190
}
190191
self.set_column(col);
191192
}
@@ -197,7 +198,7 @@ impl Renderer {
197198
pub fn move_down_by(&mut self, cells: u16) {
198199
self.current_row += cells as usize;
199200
while self.current_row >= self.rows.len() {
200-
self.rows.push(Row::new());
201+
self.rows.push_back(Row::new());
201202
}
202203
}
203204

@@ -219,7 +220,18 @@ impl Renderer {
219220
let _ = &row.cells[..row.position];
220221
}
221222

222-
pub fn emit_html<W: std::io::Write>(&self, mut out: W) -> std::io::Result<()> {
223+
pub fn pop_completed_row(&mut self) -> Option<Vec<u8>> {
224+
if self.rows.len() > 64 {
225+
self.remove_oldest_row()
226+
} else {
227+
None
228+
}
229+
}
230+
231+
pub fn remove_oldest_row(&mut self) -> Option<Vec<u8>> {
232+
use std::io::Write;
233+
let mut out = Vec::new();
234+
223235
let mut prev = Cell {
224236
text: ' ',
225237
foreground: Color::bright_white(),
@@ -230,32 +242,36 @@ impl Renderer {
230242
dim: false,
231243
};
232244

233-
out.write_all(b"<span>")?;
234-
235-
for row in &self.rows[..self.current_row] {
236-
let row = &*row;
237-
for cell in &row.cells {
238-
// Terminal applications will often reset the style right after some formatted text
239-
// then write some whitespace then set it to something again.
240-
// So we only apply style changes if the cell is nonempty. This is a ~50% savings
241-
// in emitted HTML.
242-
if cell.text != ' ' {
243-
if cell.bold != prev.bold || cell.foreground != prev.foreground {
244-
self.styles.with(cell.foreground, cell.bold, |class| {
245+
out.extend(b"<span>");
246+
247+
let row = self.rows.pop_front()?;
248+
self.current_row = self.current_row.saturating_sub(1);
249+
250+
for cell in &row.cells {
251+
// Terminal applications will often reset the style right after some formatted text
252+
// then write some whitespace then set it to something again.
253+
// So we only apply style changes if the cell is nonempty. This is a ~50% savings
254+
// in emitted HTML.
255+
if cell.text != ' ' {
256+
if cell.bold != prev.bold || cell.foreground != prev.foreground {
257+
self.styles
258+
.with(cell.foreground, cell.bold, |class| {
245259
write!(out, "</span><span class='{}'>", class)
246-
})?;
247-
}
248-
prev = cell.clone();
249-
}
250-
match cell.text {
251-
'<' => out.write_all(b"&lt")?,
252-
'>' => out.write_all(b"&gt")?,
253-
c => write!(out, "{}", c)?,
260+
})
261+
.unwrap();
254262
}
263+
prev = cell.clone();
264+
}
265+
match cell.text {
266+
'<' => out.extend(b"&lt"),
267+
'>' => out.extend(b"&gt"),
268+
c => write!(out, "{}", c).unwrap(),
255269
}
256-
out.write_all(&[b'\n'])?;
257270
}
258-
out.write_all(b"</span>")
271+
out.extend(&[b'\n']);
272+
out.extend(b"</span>");
273+
274+
Some(out)
259275
}
260276

261277
pub fn emit_css<W: std::io::Write>(&self, mut out: W) -> std::io::Result<()> {
@@ -271,12 +287,4 @@ impl Renderer {
271287

272288
Ok(())
273289
}
274-
275-
/*
276-
pub fn clear(&mut self) {
277-
self.rows.clear();
278-
self.rows.push(Row::new());
279-
self.current_row = 0;
280-
}
281-
*/
282290
}

ci-crates

Lines changed: 0 additions & 3 deletions
This file was deleted.

ci.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ then
2323
else
2424
group cargo build
2525
group cargo run -- sync --tool="$1" --bucket=miri-bot-dev
26-
group cargo run -- run --tool="$1" --bucket=miri-bot-dev --crate-list=ci-crates --rerun
26+
group cargo run -- run --tool="$1" --bucket=miri-bot-dev --crates=10
2727
fi

server/.cargo/config.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[target.aarch64-unknown-linux-musl]
2+
linker = "aarch64-linux-gnu-gcc"

server/404.html

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<!DOCTYPE HTML>
2+
<html><head><style>
3+
body {
4+
background: #111;
5+
color: #eee;
6+
}
7+
pre {
8+
word-wrap: break-word;
9+
white-space: pre-wrap;
10+
font-size: 14px;
11+
font-size-adjust: none;
12+
text-size-adjust: none;
13+
-webkit-text-size-adjust: 100%;
14+
-moz-text-size-adjust: 100%;
15+
-ms-text-size-adjust: 100%;
16+
}
17+
</style><title>oops</title></head>
18+
<body><pre><span style='color:#f55; font-weight:bold'>error</span>: No such file or directory (http error 404)
19+
20+
<span style='color:#f55; font-weight:bold'>error</span>: aborting due to previous error</pre></body></html>

0 commit comments

Comments
 (0)