Byte ranges are properly advertised and served.
This means Firefox can seek video / audio served by us. To think, you used to need Flash to do this.main
parent
aa183de15e
commit
1e6c29fb01
|
@ -1,6 +1,7 @@
|
||||||
use std::{
|
use std::{
|
||||||
|
cmp::{min, max},
|
||||||
collections::*,
|
collections::*,
|
||||||
convert::Infallible,
|
convert::{Infallible, TryInto},
|
||||||
error::Error,
|
error::Error,
|
||||||
io::SeekFrom,
|
io::SeekFrom,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
@ -31,9 +32,11 @@ use ptth::http_serde;
|
||||||
|
|
||||||
fn parse_range_header (range_str: &str) -> (Option <u64>, Option <u64>) {
|
fn parse_range_header (range_str: &str) -> (Option <u64>, Option <u64>) {
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref RE: Regex = Regex::new (r"^(\d+)-(\d+)$").expect ("Couldn't compile regex for Range header");
|
static ref RE: Regex = Regex::new (r"^bytes=(\d*)-(\d*)$").expect ("Couldn't compile regex for Range header");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println! ("{}", range_str);
|
||||||
|
|
||||||
let caps = match RE.captures (range_str) {
|
let caps = match RE.captures (range_str) {
|
||||||
Some (x) => x,
|
Some (x) => x,
|
||||||
_ => return (None, None),
|
_ => return (None, None),
|
||||||
|
@ -100,7 +103,7 @@ async fn main () -> Result <(), Box <dyn Error>> {
|
||||||
|
|
||||||
for (k, v) in parts.headers.iter () {
|
for (k, v) in parts.headers.iter () {
|
||||||
let v = std::str::from_utf8 (v).unwrap ();
|
let v = std::str::from_utf8 (v).unwrap ();
|
||||||
println! ("{}: {}", k, v);
|
//println! ("{}: {}", k, v);
|
||||||
|
|
||||||
if k == "range" {
|
if k == "range" {
|
||||||
let (start, end) = parse_range_header (v);
|
let (start, end) = parse_range_header (v);
|
||||||
|
@ -114,7 +117,7 @@ async fn main () -> Result <(), Box <dyn Error>> {
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
println! ("Step 6");
|
//println! ("Step 6");
|
||||||
let client = client.clone ();
|
let client = client.clone ();
|
||||||
tokio::spawn (async move {
|
tokio::spawn (async move {
|
||||||
let (tx, rx) = channel (2);
|
let (tx, rx) = channel (2);
|
||||||
|
@ -129,11 +132,18 @@ async fn main () -> Result <(), Box <dyn Error>> {
|
||||||
path.push (&uri [1..]);
|
path.push (&uri [1..]);
|
||||||
let mut f = File::open (path).await.unwrap ();
|
let mut f = File::open (path).await.unwrap ();
|
||||||
|
|
||||||
if let Some (start) = range_start {
|
|
||||||
f.seek (SeekFrom::Start (start)).await.unwrap ();
|
|
||||||
}
|
|
||||||
|
|
||||||
let file_md = f.metadata ().await.unwrap ();
|
let file_md = f.metadata ().await.unwrap ();
|
||||||
|
let file_len = file_md.len ();
|
||||||
|
|
||||||
|
let start = range_start.unwrap_or (0);
|
||||||
|
let end = range_end.unwrap_or (file_len);
|
||||||
|
|
||||||
|
let start = max (0, min (start, file_len));
|
||||||
|
let end = max (0, min (end, file_len));
|
||||||
|
|
||||||
|
f.seek (SeekFrom::Start (start)).await.unwrap ();
|
||||||
|
|
||||||
|
println! ("Serving range {}-{}", start, end);
|
||||||
|
|
||||||
if should_send_body {
|
if should_send_body {
|
||||||
tokio::spawn (async move {
|
tokio::spawn (async move {
|
||||||
|
@ -141,12 +151,15 @@ async fn main () -> Result <(), Box <dyn Error>> {
|
||||||
|
|
||||||
let mut tx = tx;
|
let mut tx = tx;
|
||||||
//let mut bytes_sent = 0;
|
//let mut bytes_sent = 0;
|
||||||
|
let mut bytes_left = end - start;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut buffer = vec! [0u8; 65_536];
|
let mut buffer = vec! [0u8; 65_536];
|
||||||
let bytes_read = f.read (&mut buffer).await.unwrap ();
|
let bytes_read: u64 = f.read (&mut buffer).await.unwrap ().try_into ().unwrap ();
|
||||||
|
|
||||||
buffer.truncate (bytes_read);
|
let bytes_read = min (bytes_left, bytes_read);
|
||||||
|
|
||||||
|
buffer.truncate (bytes_read.try_into ().unwrap ());
|
||||||
|
|
||||||
if bytes_read == 0 {
|
if bytes_read == 0 {
|
||||||
break;
|
break;
|
||||||
|
@ -156,6 +169,11 @@ async fn main () -> Result <(), Box <dyn Error>> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bytes_left -= bytes_read;
|
||||||
|
if bytes_left == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
//bytes_sent += bytes_read;
|
//bytes_sent += bytes_read;
|
||||||
//println! ("Sent {} bytes", bytes_sent);
|
//println! ("Sent {} bytes", bytes_sent);
|
||||||
|
|
||||||
|
@ -165,14 +183,21 @@ async fn main () -> Result <(), Box <dyn Error>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut headers: HashMap <String, Vec <u8>> = Default::default ();
|
let mut headers: HashMap <String, Vec <u8>> = Default::default ();
|
||||||
//headers.insert (String::from ("x-its-a-header"), Vec::from (&b"wow"[..]));
|
headers.insert (String::from ("accept-ranges"), b"bytes".to_vec ());
|
||||||
|
|
||||||
|
let status_code;
|
||||||
|
|
||||||
if range_start.is_none () && range_end.is_none () {
|
if range_start.is_none () && range_end.is_none () {
|
||||||
headers.insert (String::from ("content-length"), file_md.len ().to_string ().into_bytes ());
|
headers.insert (String::from ("content-length"), end.to_string ().into_bytes ());
|
||||||
|
status_code = http_serde::StatusCode::Ok;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
headers.insert (String::from ("content-range"), format! ("bytes {}-{}/{}", start, end - 1, end).into_bytes ());
|
||||||
|
status_code = http_serde::StatusCode::PartialContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
let resp_parts = http_serde::ResponseParts {
|
let resp_parts = http_serde::ResponseParts {
|
||||||
status_code: http_serde::StatusCode::Ok,
|
status_code,
|
||||||
headers,
|
headers,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -184,7 +209,7 @@ async fn main () -> Result <(), Box <dyn Error>> {
|
||||||
resp_req = resp_req.body (body);
|
resp_req = resp_req.body (body);
|
||||||
}
|
}
|
||||||
|
|
||||||
println! ("Step 6");
|
//println! ("Step 6");
|
||||||
if let Err (e) = resp_req.send ().await {
|
if let Err (e) = resp_req.send ().await {
|
||||||
println! ("Err: {:?}", e);
|
println! ("Err: {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue